[
  {
    "path": ".editorconfig",
    "content": "; This file is for unifying the coding style for different editors and IDEs.\n; More information at http://editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\nindent_size = 4\nindent_style = space\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Path-based git attributes\n# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html\n\n# Ignore all test and documentation with \"export-ignore\".\n/.github            export-ignore\n/.gitattributes     export-ignore\n/.gitignore         export-ignore\n/phpunit.xml.dist   export-ignore\n/art                export-ignore\n/docs               export-ignore\n/tests              export-ignore\n/.editorconfig      export-ignore\n/.php_cs.dist.php   export-ignore\n/phpstan*           export-ignore\n/CHANGELOG.md       export-ignore\n/CONTRIBUTING.md    export-ignore\n\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: spatie\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1_Bug_report.yml",
    "content": "name: Bug Report\ndescription: \"Report a reproducible bug.\"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n          Before creating a new Bug Report, please check that there isn't already a similar issue on [the issue tracker](https://github.com/spatie/laravel-permission/issues) or in [the discussions](https://github.com/spatie/laravel-permission/discussions).\n          Also, **many issues/questions/problems are already answered** in the [documentation](https://spatie.be/docs/laravel-permission) already. **Please be sure to check the docs** because it will save you time!\n\n  - type: textarea\n    attributes:\n      label: Description\n      description: A clear and concise description of what the bug is.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Steps To Reproduce\n      description: How do you trigger this bug? Please walk us through it step by step.\n      value: |\n        1.\n        2.\n        3.\n        ...\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Example Application\n      description: \"Here is a link to my Github repo containing a minimal Laravel application which shows my problem:\"\n  - type: markdown\n    attributes:\n      value: |\n          You can use `composer show` to get package version numbers:\n  - type: input\n    attributes:\n      label: \"Version of spatie/laravel-permission package:\"\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: \"Version of laravel/framework package:\"\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: \"PHP version:\"\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: \"Database engine and version:\"\n  - type: input\n    attributes:\n      label: \"OS: Windows/Mac/Linux version:\" \n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\ncontact_links:\n    - name: Feature Request\n      url: https://github.com/spatie/laravel-permission/discussions/new?category=ideas\n      about: Share ideas for new features\n    - name: Ask a Question\n      url: https://github.com/spatie/laravel-permission/discussions/new?category=q-a\n      about: Ask the community for help\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\n\nupdates:\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/dependabot-auto-merge.yml",
    "content": "name: dependabot-auto-merge\non: pull_request_target\n\npermissions:\n  pull-requests: write\n  contents: write\n\njobs:\n  dependabot:\n    runs-on: ubuntu-latest\n    if: ${{ github.actor == 'dependabot[bot]' }}\n    steps:\n\n      - name: Dependabot metadata\n        id: metadata\n        uses: dependabot/fetch-metadata@v2\n        with:\n          github-token: \"${{ secrets.GITHUB_TOKEN }}\"\n          compat-lookup: true\n\n      - name: Auto-merge Dependabot PRs for semver-minor updates\n        if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}}\n        run: gh pr merge --auto --merge \"$PR_URL\"\n        env:\n          PR_URL: ${{github.event.pull_request.html_url}}\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n\n      - name: Auto-merge Dependabot PRs for semver-patch updates\n        if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}\n        run: gh pr merge --auto --merge \"$PR_URL\"\n        env:\n          PR_URL: ${{github.event.pull_request.html_url}}\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n\n      - name: Auto-merge Dependabot PRs for Action major versions when compatibility is higher than 90%\n        if: ${{steps.metadata.outputs.package-ecosystem == 'github_actions' && steps.metadata.outputs.update-type == 'version-update:semver-major' && steps.metadata.outputs.compatibility-score >= 90}}\n        run: gh pr merge --auto --merge \"$PR_URL\"\n        env:\n          PR_URL: ${{github.event.pull_request.html_url}}\n          GH_TOKEN: ${{secrets.GITHUB_TOKEN}}\n"
  },
  {
    "path": ".github/workflows/fix-php-code-style-issues.yml",
    "content": "name: Fix PHP code style issues\n\non:\n  push:\n    paths:\n      - '**.php'\n\njobs:\n  php-code-styling:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n        with:\n          ref: ${{ github.head_ref }}\n\n      - name: Fix PHP code style issues\n        uses: aglipanci/laravel-pint-action@v2\n\n      - name: Commit changes\n        uses: stefanzweifel/git-auto-commit-action@v7\n        with:\n          commit_message: Fix styling\n"
  },
  {
    "path": ".github/workflows/phpstan.yml",
    "content": "name: PHPStan\n\non:\n    push:\n        paths:\n            - '**.php'\n            - 'phpstan.neon.dist'\n    pull_request:\n        paths:\n            - '**.php'\n            - 'phpstan.neon.dist'\n\njobs:\n    phpstan:\n        name: phpstan\n        runs-on: ubuntu-latest\n        steps:\n            - uses: actions/checkout@v6\n\n            - name: Setup PHP\n              uses: shivammathur/setup-php@v2\n              with:\n                  php-version: 8.4\n                  coverage: none\n\n            - name: Install composer dependencies\n              uses: ramsey/composer-install@v4\n\n            - name: Install larastan\n              run: |\n                  composer require \"larastan/larastan\" --no-interaction --no-update\n                  composer update --prefer-dist --no-interaction\n\n            - name: Run PHPStan\n              run: ./vendor/bin/phpstan --error-format=github\n"
  },
  {
    "path": ".github/workflows/run-tests.yml",
    "content": "name: Run Tests - Current\n\non:\n  - push\n  - pull_request\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        php: [8.5, 8.4]\n        laravel: [\"^13\", \"^12\"]\n        dependency-version: [prefer-stable, prefer-lowest]\n        include:\n          - laravel: \"^13\"\n            testbench: 11.*\n          - laravel: \"^12\"\n            testbench: 10.*\n\n    name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }}\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php }}\n          extensions: curl, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, iconv\n          coverage: none\n\n      - name: Install dependencies\n        run: |\n          composer require \"laravel/framework:${{ matrix.laravel }}\" \"orchestra/testbench:${{ matrix.testbench }}\" \"symfony/console:>=4.3.4\" \"mockery/mockery:^1.3.2\" \"nesbot/carbon:>=2.72.6\" --no-interaction --no-update\n          composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction\n\n      - name: Execute tests\n        run: vendor/bin/pest\n"
  },
  {
    "path": ".github/workflows/test-cache-drivers.yml",
    "content": "name: \"Run Tests - Cache Drivers\"\n\non: [push, pull_request]\n\njobs:\n  cache:\n\n    runs-on: ubuntu-latest\n\n    services:\n      redis:\n        image: redis\n        ports:\n          - 6379/tcp\n        options: --health-cmd=\"redis-cli ping\" --health-interval=10s --health-timeout=5s --health-retries=3\n\n    strategy:\n      fail-fast: false\n\n    name: Cache Drivers\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: 8.4\n          extensions: curl, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, iconv, memcache\n          coverage: none\n\n      - name: Install dependencies\n        run: |\n          composer require \"predis/predis\" --no-interaction --no-update\n          composer update --prefer-stable --prefer-dist --no-interaction\n\n      - name: Execute tests - memcached cache driver\n        run: |\n          vendor/bin/pest\n        env:\n          CACHE_DRIVER: memcached\n\n      - name: Execute tests - redis cache driver\n        run: |\n          vendor/bin/pest\n        env:\n          CACHE_DRIVER: redis\n          REDIS_PORT: ${{ job.services.redis.ports['6379'] }}\n\n      - name: Execute tests - database cache driver\n        run: |\n          vendor/bin/pest\n        env:\n          CACHE_DRIVER: database\n\n      - name: Execute tests - file cache driver\n        run: |\n          vendor/bin/pest\n        env:\n          CACHE_DRIVER: file\n\n      - name: Execute tests - array cache driver\n        run: |\n          vendor/bin/pest\n        env:\n          CACHE_DRIVER: array\n"
  },
  {
    "path": ".github/workflows/update-changelog.yml",
    "content": "name: \"Update Changelog\"\n\non:\n  release:\n    types: [released]\n\njobs:\n  update:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n        with:\n          ref: main\n\n      - name: Update Changelog\n        uses: stefanzweifel/changelog-updater-action@v1\n        with:\n          latest-version: ${{ github.event.release.name }}\n          release-notes: ${{ github.event.release.body }}\n\n      - name: Commit updated CHANGELOG\n        uses: stefanzweifel/git-auto-commit-action@v7\n        with:\n          branch: main\n          commit_message: Update CHANGELOG\n          file_pattern: CHANGELOG.md\n"
  },
  {
    "path": ".gitignore",
    "content": "build\ncomposer.lock\nvendor\ntests/temp\n.idea\n.phpunit.cache\n.phpunit.result.cache\n.php-cs-fixer.cache\ntests/TestSupport/CreatePermissionCustomTables.php\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to `laravel-permission` will be documented in this file\n\n## 7.2.4 - 2026-03-17\n\n### What's Changed\n\n* Internals only.\n* Bump ramsey/composer-install from 3 to 4 by @dependabot[bot] in https://github.com/spatie/laravel-permission/pull/2936\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/7.2.3...7.2.4\n\n## 6.25.0 - 2026-03-17\n\n### What's Changed\n\n* Add Laravel 13 compatibility to old branch, so that 3rd party packages can support L13 without urgent upgrading\n* Convert test suite from PHPUnit to Pest by @freekmurze in https://github.com/spatie/laravel-permission/pull/2912\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.24.1...6.25.0\n\n## 7.2.3 - 2026-02-23\n\n- Update config comments to point to new v7 event class names\n\n## 7.2.2 - 2026-02-22\n\n### What's Changed\n\n* Clear wildcard permission index when assigning or removing roles by @freekmurze in https://github.com/spatie/laravel-permission/pull/2925\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/7.2.1...7.2.2\n\n## 7.2.1 - 2026-02-21\n\n### What's Changed\n\n- Add Laravel 13 support\n- Upgrade to laravel/passport ^13.0\n- Drop prefer-lowest from CI matrix\n\n## 7.2.0 - 2026-02-18\n\n### What's Changed\n\n* Fix: do not treat string '0' as empty role/permission input by @laraib15 in https://github.com/spatie/laravel-permission/pull/2916\n\n### New Contributors\n\n* @laraib15 made their first contribution in https://github.com/spatie/laravel-permission/pull/2916\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/7.1.0...7.2.0\n\n## 7.1.0 - 2026-02-14\n\n### What's Changed\n\n* [v7] Bring back support for PHP 8.3 by @ssw1cblarrion in https://github.com/spatie/laravel-permission/pull/2918\n\n### New Contributors\n\n* @ssw1cblarrion made their first contribution in https://github.com/spatie/laravel-permission/pull/2918\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/7.0.0...7.1.0\n\n## 7.0.0 - 2026-02-11\n\n### Modernize codebase for v7\n\nThe aim for v7 is to modernize the codebase while keeping the upgrade path easy. Modern PHP/Laravel features, Pest tests, but no big architectural changes. A future v8 could then tackle larger changes and streamline the package more fundamentally.\n\n#### Version requirements\n\n- Requires PHP ^8.4 and Laravel ^12.0\n- Test suite uses Pest ^3.0\n\n#### Service provider\n\n- Converted to `PackageServiceProvider` from `spatie/laravel-package-tools`\n- Removed Lumen support\n\n#### Class renames\n\n- Event classes now have an `Event` suffix (`PermissionAttached` → `PermissionAttachedEvent`, etc.)\n- Command classes now have a `Command` suffix (`CacheReset` → `CacheResetCommand`, etc.)\n\n#### Type safety\n\n- Added return types and parameter types throughout traits, middleware, exceptions, contracts, and commands\n\n#### Code modernization\n\n- `is_a($this, X::class)` → `$this instanceof X`\n- `get_class($obj)` → `$obj::class`\n- `strpos($x, $y) !== false` → `str_contains($x, $y)`\n- Constructor promotion in `WildcardPermission`\n- Proper `use` imports for global classes\n\n#### Cleanup\n\n- Removed deprecated `clearClassPermissions()` method\n- Removed `__construct` from `Wildcard` contract\n- Modernized migration stubs\n\n#### Testing\n\n- Converted entire test suite from PHPUnit to Pest (#2912)\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.24.1...7.0.0\n\n## 6.24.1 - 2026-02-09\n\n### What's Changed\n\n* Add team support to permission:assign-role command by @freekmurze in https://github.com/spatie/laravel-permission/pull/2910\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.24.0...6.24.1\n\n## 6;24.1 - 2026-02-09\n\n### What's Changed\n\n* Add team support to permission:assign-role command by @freekmurze in https://github.com/spatie/laravel-permission/pull/2910\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.24.0...6;24.1\n\n## 6.24.0 - 2025-12-13\n\n### What's Changed\n\n* Add BackedEnum Support to RoleOrPermissionMiddleware by @imhayatunnabi in https://github.com/spatie/laravel-permission/pull/2890\n* Add Missing roleOrPermission() route macro by @imhayatunnabi in https://github.com/spatie/laravel-permission/pull/2893\n* PHP 8.5: Fix deprecated usage of `null` as array key by @jnoordsij in https://github.com/spatie/laravel-permission/pull/2904\n\n### Internals/Testing\n\n* Skip prefer-lowest dependency version for Laravel 11 by @drbyte in https://github.com/spatie/laravel-permission/pull/2903\n* Bump actions/checkout from 5 to 6 by @dependabot[bot] in https://github.com/spatie/laravel-permission/pull/2901\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.23.0...6.24.0\n\n## 6.23.0 - 2025-11-03\n\n### What's Changed\n\n* Performance enhancement: Reduce unnecessary container resolution calls by @imhayatunnabi in https://github.com/spatie/laravel-permission/pull/2889\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.22.0...6.23.0\n\n## 6.22.0 - 2025-10-27\n\n### What's Changed\n\n* Dispatch RoleDetached on syncRoles when events are enabled by @josedaian in https://github.com/spatie/laravel-permission/pull/2869\n* Refactor exception handling in migration stub by @alisalehi1380 in https://github.com/spatie/laravel-permission/pull/2886\n* Fix TOCTOU race condition in permission loading for concurrent (Octane etc) environments by @imhayatunnabi in https://github.com/spatie/laravel-permission/pull/2883\n* Add assign-role command by @sediqzada94 in https://github.com/spatie/laravel-permission/pull/2834\n* Test PHP 8.5 by @erikn69 in https://github.com/spatie/laravel-permission/pull/2880\n* Bump stefanzweifel/git-auto-commit-action from 6 to 7 by @dependabot[bot] in https://github.com/spatie/laravel-permission/pull/2882\n* Update issue template by @AlexVanderbist in https://github.com/spatie/laravel-permission/pull/2875\n* Bump actions/checkout from 4 to 5 by @dependabot[bot] in https://github.com/spatie/laravel-permission/pull/2870\n* Quick Panel (TALL Flowbite Starter Kit) by @aliqasemzadeh in https://github.com/spatie/laravel-permission/pull/2881\n\n### New Contributors\n\n* @josedaian made their first contribution in https://github.com/spatie/laravel-permission/pull/2869\n* @alisalehi1380 made their first contribution in https://github.com/spatie/laravel-permission/pull/2886\n* @imhayatunnabi made their first contribution in https://github.com/spatie/laravel-permission/pull/2883\n* @sediqzada94 made their first contribution in https://github.com/spatie/laravel-permission/pull/2834\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.21.0...6.22.0\n\n## 6.21.0 - 2025-07-23\n\n### What's Changed\n\n* Allow removing multiple roles with the `removeRole` method by @TobMoeller in https://github.com/spatie/laravel-permission/pull/2859\n* [Docs] Correct middleware order for documentation example in `teams-permissions.md` by @dualklip in https://github.com/spatie/laravel-permission/pull/2863\n\n### New Contributors\n\n* @dualklip made their first contribution in https://github.com/spatie/laravel-permission/pull/2863\n* @TobMoeller made their first contribution in https://github.com/spatie/laravel-permission/pull/2859\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.20.0...6.21.0\n\n## 6.20.0 - 2025-06-14\n\n### What's Changed\n\n* Add translations support for exception messages by @nAa6666 in https://github.com/spatie/laravel-permission/pull/2852\n\n### New Contributors\n\n* @nAa6666 made their first contribution in https://github.com/spatie/laravel-permission/pull/2852\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.19.0...6.20.0\n\n## 6.19.0 - 2025-05-31\n\n### What's Changed\n\n* Revert \"Remove `collectPermissions` that is not being assigned\" by @erikn69 in https://github.com/spatie/laravel-permission/pull/2851\n* Fix guard_name not used to set default attribute in Role and Permission model by @Ken-vdE in https://github.com/spatie/laravel-permission/pull/2837\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.18.0...6.19.0\n\n## 6.18.0 - 2025-05-14\n\n### What's Changed\n\n* refactor exception throwing in migration file to use throw_if() by @ccaioadriano in https://github.com/spatie/laravel-permission/pull/2819\n* Fix: Example in config comment includes `permission.` prefix on `wildcard_permission` key by @jerrens in https://github.com/spatie/laravel-permission/pull/2835\n* Fix commented config key typo by @erikn69 in https://github.com/spatie/laravel-permission/pull/2844\n* Remove `collectPermissions` that is not being assigned by @JHWelch in https://github.com/spatie/laravel-permission/pull/2840\n* [Docs] Update multiple-guards.md by @Ken-vdE in https://github.com/spatie/laravel-permission/pull/2836\n* [Docs] Remove extra period by @coreyhn in https://github.com/spatie/laravel-permission/pull/2841\n* Add JetAdmin as UI Option. by @aliqasemzadeh in https://github.com/spatie/laravel-permission/pull/2814\n\n### New Contributors\n\n* @ccaioadriano made their first contribution in https://github.com/spatie/laravel-permission/pull/2819\n* @coreyhn made their first contribution in https://github.com/spatie/laravel-permission/pull/2841\n* @jerrens made their first contribution in https://github.com/spatie/laravel-permission/pull/2835\n* @Ken-vdE made their first contribution in https://github.com/spatie/laravel-permission/pull/2836\n* @JHWelch made their first contribution in https://github.com/spatie/laravel-permission/pull/2840\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.17.0...6.18.0\n\n## 6.17.0 - 2025-04-09\n\n### What's Changed\n\n* Route macro functions: add backed enum support by @Yi-pixel in https://github.com/spatie/laravel-permission/pull/2823\n\n### New Contributors\n\n* @Yi-pixel made their first contribution in https://github.com/spatie/laravel-permission/pull/2823\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.16.0...6.17.0\n\n## 6.16.0 - 2025-02-28\n\n### What's Changed\n\n* Middleware: support enums in role/permission middleware by @marklawntalk in https://github.com/spatie/laravel-permission/pull/2813\n\n### New Contributors\n\n* @marklawntalk made their first contribution in https://github.com/spatie/laravel-permission/pull/2813\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.15.0...6.16.0\n\n## 6.15.0 - 2025-02-17\n\n### What's Changed\n\n* Added 4 events for adding and removing roles or permissions by @sven-wegner in https://github.com/spatie/laravel-permission/pull/2742\n* Fixed bug of loading user roles of different teams to current team by @mohamedds-12 in https://github.com/spatie/laravel-permission/pull/2803\n\n### New Contributors\n\n* @sven-wegner made their first contribution in https://github.com/spatie/laravel-permission/pull/2742\n* @mohamedds-12 made their first contribution in https://github.com/spatie/laravel-permission/pull/2803\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.14.0...6.15.0\n\n## 6.14.0 - 2025-02-13\n\n### What's Changed\n\n* LDAP model lookup from Auth Provider by @crossplatformconsulting in https://github.com/spatie/laravel-permission/pull/2750\n\n### Internals\n\n* Add PHPUnit annotations, for future compatibility with PHPUnit 12 by @drbyte in https://github.com/spatie/laravel-permission/pull/2806\n\n### New Contributors\n\n* @crossplatformconsulting made their first contribution in https://github.com/spatie/laravel-permission/pull/2750\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.13.0...6.14.0\n\n## 6.13.0 - 2025-02-05\n\n### What's Changed\n\n* LazyLoading: Explicitly call `loadMissing('permissions')` when the relation is needed, and test with `Model::preventLazyLoading()` by @erikn69 in https://github.com/spatie/laravel-permission/pull/2776\n* [Docs] Add instructions to reinitialize cache for multi-tenancy key settings when updating multiple tenants in a single request cycle, by @sudkumar in https://github.com/spatie/laravel-permission/pull/2804\n\n### New Contributors\n\n* @sudkumar made their first contribution in https://github.com/spatie/laravel-permission/pull/2804\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.12.0...6.13.0\n\n## 6.12.0 - 2025-01-31\n\n### What's Changed\n\n* Support Laravel 12\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.11.0...6.12.0\n\n## 6.11.0 - 2025-01-30\n\n### What's Changed\n\n* Add configurable team resolver for permission team id (helpful for Jetstream, etc) by @adrenallen in https://github.com/spatie/laravel-permission/pull/2790\n\n### Internals\n\n* Replace php-cs-fixer with Laravel Pint by @bobbrodie in https://github.com/spatie/laravel-permission/pull/2780\n\n### Documentation Updates\n\n* [Docs] Include namespace in example in uuid.md by @ken-tam in https://github.com/spatie/laravel-permission/pull/2764\n* [Docs] Include Laravel 11 example in exceptions.md by @frankliniwobi in https://github.com/spatie/laravel-permission/pull/2768\n* [Docs] Fix typo in code example in passport.md by @m3skalina in https://github.com/spatie/laravel-permission/pull/2782\n* [Docs] Correct username in new-app.md by @trippodi in https://github.com/spatie/laravel-permission/pull/2785\n* [Docs] Add composer specificity by @imanghafoori1 in https://github.com/spatie/laravel-permission/pull/2772\n* [Docs] Update installation-laravel.md to fix providers.php location. by @curiousteam in https://github.com/spatie/laravel-permission/pull/2796\n\n### New Contributors\n\n* @ken-tam made their first contribution in https://github.com/spatie/laravel-permission/pull/2764\n* @frankliniwobi made their first contribution in https://github.com/spatie/laravel-permission/pull/2768\n* @bobbrodie made their first contribution in https://github.com/spatie/laravel-permission/pull/2780\n* @m3skalina made their first contribution in https://github.com/spatie/laravel-permission/pull/2782\n* @trippodi made their first contribution in https://github.com/spatie/laravel-permission/pull/2785\n* @imanghafoori1 made their first contribution in https://github.com/spatie/laravel-permission/pull/2772\n* @curiousteam made their first contribution in https://github.com/spatie/laravel-permission/pull/2796\n* @adrenallen made their first contribution in https://github.com/spatie/laravel-permission/pull/2790\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.10.1...6.11.0\n\n## 6.10.1 - 2024-11-08\n\n### What's Changed\n\n* Fix #2749 regression bug in `6.10.0` : \"Can no longer delete permissions\" by @erikn69 in https://github.com/spatie/laravel-permission/pull/2759\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.10.0...6.10.1\n\n## 6.10.0 - 2024-11-05\n\n### What's Changed\n\n* Fix `GuardDoesNotMatch should accept collection` by @erikn69 in https://github.com/spatie/laravel-permission/pull/2748\n* Improve performance for hydrated collections by @inserve-paul in https://github.com/spatie/laravel-permission/pull/2749\n* Only show error if `cache key exists` and `forgetCachedPermissions` fails by @erikn69 in https://github.com/spatie/laravel-permission/pull/2707\n* Remove v5 cache fallback alias by @drbyte in https://github.com/spatie/laravel-permission/pull/2754\n* Include `Larastan` in `dev` by @drbyte in https://github.com/spatie/laravel-permission/pull/2755\n\n#### Docs\n\n* [Docs example] Check for 'all' or 'any' permissions before specific permissions by @ceilidhboy in https://github.com/spatie/laravel-permission/pull/2694\n* [Docs] Fix typo in uuid.md by @levizoesch in https://github.com/spatie/laravel-permission/pull/2705\n* [Docs] Upgrade Guide - Add PR links to upgrade guide by @mraheelkhan in https://github.com/spatie/laravel-permission/pull/2716\n* [Docs] use more modern syntax for nullable return type by @galangaidilakbar in https://github.com/spatie/laravel-permission/pull/2719\n* [Docs] camelCase variable naming in example by @KamilWojtalak in https://github.com/spatie/laravel-permission/pull/2723\n* [Docs] Update using-policies.md by @marcleonhard in https://github.com/spatie/laravel-permission/pull/2741\n* [Docs] Example of pushing custom middleware before SubstituteBindings middleware by @WyattCast44 in https://github.com/spatie/laravel-permission/pull/2740\n\n#### Other\n\n* PHP 8.4 tests by @erikn69 in https://github.com/spatie/laravel-permission/pull/2747\n* Fix comment typo by @machacekmartin in https://github.com/spatie/laravel-permission/pull/2753\n\n### New Contributors\n\n* @ceilidhboy made their first contribution in https://github.com/spatie/laravel-permission/pull/2694\n* @levizoesch made their first contribution in https://github.com/spatie/laravel-permission/pull/2705\n* @galangaidilakbar made their first contribution in https://github.com/spatie/laravel-permission/pull/2719\n* @KamilWojtalak made their first contribution in https://github.com/spatie/laravel-permission/pull/2723\n* @marcleonhard made their first contribution in https://github.com/spatie/laravel-permission/pull/2741\n* @WyattCast44 made their first contribution in https://github.com/spatie/laravel-permission/pull/2740\n* @inserve-paul made their first contribution in https://github.com/spatie/laravel-permission/pull/2749\n* @machacekmartin made their first contribution in https://github.com/spatie/laravel-permission/pull/2753\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.9.0...6.10.0\n\n## 6.9.0 - 2024-06-22\n\n### What's Changed\n\n* Use `->withPivot()` for teamed relationships (allows `getPivotColumns()`) by @juliangums in https://github.com/spatie/laravel-permission/pull/2679\n* Update docblock on `$role->hasPermissionTo()` to include `BackedEnum` by @drbyte co-authored by @SanderMuller\n* [Docs] Clarify that `$guard_name` can be an array by @angelej in https://github.com/spatie/laravel-permission/pull/2659\n* Fix misc typos in changelog by @szepeviktor in https://github.com/spatie/laravel-permission/pull/2686\n\n### New Contributors\n\n* @angelej made their first contribution in https://github.com/spatie/laravel-permission/pull/2659\n* @SanderMuller made their first contribution in #2676\n* @szepeviktor made their first contribution in https://github.com/spatie/laravel-permission/pull/2686\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.8.0...6.9.0\n\n## 6.8.0 - 2024-06-21\n\n### What's Changed\n\n* Fix can't save the same model twice by @erikn69 in https://github.com/spatie/laravel-permission/pull/2658\n* Fix phpstan from #2616 by @erikn69 in https://github.com/spatie/laravel-permission/pull/2685\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.7.0...6.8.0\n\n## 6.7.0 - 2024-04-19\n\n### What's Changed\n\n- Fixed remaining Octane event contract. Update to #2656 in release `6.5.0`\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.6.0...6.7.0\n\n## 6.6.0 - 2024-04-19\n\n### What's Changed\n\n* Roles: Support for casting role names to enums by @gajosadrian in https://github.com/spatie/laravel-permission/pull/2616\n* Fix permission:show UUID error #2581 by @drbyte in https://github.com/spatie/laravel-permission/pull/2582\n* Cover WildcardPermission instance verification based on its own guard (Allow hasAllPermissions and hasAnyPermission to run on custom guard for WildcardPermission) by @AlexandreBellas in https://github.com/spatie/laravel-permission/pull/2608\n* Register Laravel \"About\" details by @drbyte in https://github.com/spatie/laravel-permission/pull/2584\n\n### New Contributors\n\n* @gajosadrian made their first contribution in https://github.com/spatie/laravel-permission/pull/2616\n* @AlexandreBellas made their first contribution in https://github.com/spatie/laravel-permission/pull/2608\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.5.0...6.6.0\n\n## 6.5.0 - 2024-04-18\n\n### What's Changed\n\n* Octane: Fix wrong event listener by @erikn69 in https://github.com/spatie/laravel-permission/pull/2656\n* Teams: Add nullable team_id by @Androlax2 in https://github.com/spatie/laravel-permission/pull/2607\n* Blade: simplify the definition of multiple Blade \"if\" directives by @alissn in https://github.com/spatie/laravel-permission/pull/2628\n* DocBlocks: Update HasPermissions::collectPermissions() docblock by @Plytas in https://github.com/spatie/laravel-permission/pull/2641\n\n#### Internals\n\n* Update role-permissions.md by @killjin in https://github.com/spatie/laravel-permission/pull/2631\n* Bump ramsey/composer-install from 2 to 3 by @dependabot in https://github.com/spatie/laravel-permission/pull/2630\n* Bump dependabot/fetch-metadata from 1 to 2 by @dependabot in https://github.com/spatie/laravel-permission/pull/2642\n\n### New Contributors\n\n* @alissn made their first contribution in https://github.com/spatie/laravel-permission/pull/2628\n* @Androlax2 made their first contribution in https://github.com/spatie/laravel-permission/pull/2607\n* @Plytas made their first contribution in https://github.com/spatie/laravel-permission/pull/2641\n* @killjin made their first contribution in https://github.com/spatie/laravel-permission/pull/2631\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.4.0...6.5.0\n\n## 6.4.0 - 2024-02-28\n\n* Laravel 11 Support\n\n### What's Changed\n\n* Add Laravel 11 to workflow run tests by @mraheelkhan in https://github.com/spatie/laravel-permission/pull/2605\n* And Passport 12\n\n### Internals\n\n* Update to use Larastan Org by @arnebr in https://github.com/spatie/laravel-permission/pull/2585\n* laravel-pint-action to major version tag by @erikn69 in https://github.com/spatie/laravel-permission/pull/2586\n\n### New Contributors\n\n* @arnebr made their first contribution in https://github.com/spatie/laravel-permission/pull/2585\n* @mraheelkhan made their first contribution in https://github.com/spatie/laravel-permission/pull/2605\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.3.0...6.4.0\n\n## 6.3.0 - 2023-12-24\n\n### What's Changed\n\n* Octane Fix: Clear wildcard permissions on Tick in https://github.com/spatie/laravel-permission/pull/2583\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.2.0...6.3.0\n\n## 6.2.0 - 2023-12-09\n\n### What's Changed\n\n* Skip duplicates on sync (was triggering Integrity Constraint Violation error) by @erikn69 in https://github.com/spatie/laravel-permission/pull/2574\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.1.0...6.2.0\n\n## 6.1.0 - 2023-11-09\n\n### What's Changed\n\n- Reset teamId on Octane by @erikn69 in https://github.com/spatie/laravel-permission/pull/2547\n  NOTE: The `\\Spatie\\Permission\\Listeners\\OctaneReloadPermissions` listener introduced in 6.0.0 is removed in 6.1.0, because the logic is directly incorporated into the ServiceProvider now.\n  \n  Thanks @jameshulse for the heads-up and code-review.\n  \n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.0.1...6.1.0\n\n## 6.0.1 - 2023-11-06\n\n### What's Changed\n\n- Provide a default team_foreign_key value in case config file isn't upgraded yet or teams feature is unused. Fixes #2535\n- [Docs] Update unsetRelation() example in teams-permissions.md by @shdehnavi in https://github.com/spatie/laravel-permission/pull/2534\n- [Docs] Update link in direct-permissions.md by @sevannerse in https://github.com/spatie/laravel-permission/pull/2539\n\n### New Contributors\n\n- @sevannerse made their first contribution in https://github.com/spatie/laravel-permission/pull/2539\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/6.0.0...6.0.1\n\n## 6.0.0 - 2023-10-25\n\n### What's Changed\n\n- Full uuid/guid/ulid support by @erikn69 in https://github.com/spatie/laravel-permission/pull/2089\n- Refactor: Change static properties to non-static by @olivernybroe in https://github.com/spatie/laravel-permission/pull/2324\n- Fix Role::withCount if belongsToMany declared by @xenaio-daniil in https://github.com/spatie/laravel-permission/pull/2280\n- Fix: Lazily bind dependencies by @olivernybroe in https://github.com/spatie/laravel-permission/pull/2321\n- Avoid loss of all permissions/roles pivots on sync error by @erikn69 in https://github.com/spatie/laravel-permission/pull/2341\n- Fix delete permissions on Permissions Model by @erikn69 in https://github.com/spatie/laravel-permission/pull/2366\n- Detach users on role/permission physical deletion by @erikn69 in https://github.com/spatie/laravel-permission/pull/2370\n- Rename clearClassPermissions method to clearPermissionsCollection by @erikn69 in https://github.com/spatie/laravel-permission/pull/2369\n- Use anonymous migrations (for L8+) by @erikn69 in https://github.com/spatie/laravel-permission/pull/2374\n- [BC] Return string on getPermissionClass(), getRoleClass() by @erikn69 in https://github.com/spatie/laravel-permission/pull/2368\n- Only offer publishing when running in console by @erikn69 in https://github.com/spatie/laravel-permission/pull/2377\n- Don't add commands in web interface context by @angeljqv in https://github.com/spatie/laravel-permission/pull/2405\n- [BC] Fix Role->hasPermissionTo() signature to match HasPermissions trait by @erikn69 in https://github.com/spatie/laravel-permission/pull/2380\n- Force that getPermissionsViaRoles, hasPermissionViaRole must be used only by authenticable by @erikn69 in https://github.com/spatie/laravel-permission/pull/2382\n- fix BadMethodCallException: undefined methods hasAnyRole, hasAnyPermissions by @erikn69 in https://github.com/spatie/laravel-permission/pull/2381\n- Add PHPStan workflow with fixes by @erikn69 in https://github.com/spatie/laravel-permission/pull/2376\n- Add BackedEnum support by @drbyte in https://github.com/spatie/laravel-permission/pull/2391\n- Drop PHP 7.3 support by @angeljqv in https://github.com/spatie/laravel-permission/pull/2388\n- Drop PHP 7.4 support by @drbyte in https://github.com/spatie/laravel-permission/pull/2485\n- Test against PHP 8.3 by @erikn69 in https://github.com/spatie/laravel-permission/pull/2512\n- Fix call to an undefined method Role::getRoleClass by @erikn69 in https://github.com/spatie/laravel-permission/pull/2411\n- Remove force loading model relationships by @erikn69 in https://github.com/spatie/laravel-permission/pull/2412\n- Test alternate cache drivers by @erikn69 in https://github.com/spatie/laravel-permission/pull/2416\n- Use attach instead of sync on traits by @erikn69 in https://github.com/spatie/laravel-permission/pull/2420\n- Fewer sqls in syncRoles, syncPermissions by @erikn69 in https://github.com/spatie/laravel-permission/pull/2423\n- Add middleware using static method by @jnoordsij in https://github.com/spatie/laravel-permission/pull/2424\n- Update PHPDocs for IDE autocompletion by @erikn69 in https://github.com/spatie/laravel-permission/pull/2437\n- [BC] Wildcard permissions algorithm performance improvements (ALERT: Breaking Changes) by @danharrin in https://github.com/spatie/laravel-permission/pull/2445\n- Add withoutRole and withoutPermission scopes by @drbyte in https://github.com/spatie/laravel-permission/pull/2463\n- Add support for service-to-service Passport client by @SuperDJ in https://github.com/spatie/laravel-permission/pull/2467\n- Register OctaneReloadPermissions listener for Laravel Octane by @erikn69 in https://github.com/spatie/laravel-permission/pull/2403\n- Add guard name to exceptions by @drbyte in https://github.com/spatie/laravel-permission/pull/2481\n- Update contracts to allow for UUID by @drbyte in https://github.com/spatie/laravel-permission/pull/2480\n- Avoid triggering eloquent.retrieved event by @erikn69 in https://github.com/spatie/laravel-permission/pull/2498\n- [BC] Rename \"Middlewares\" namespace to \"Middleware\" by @drbyte in https://github.com/spatie/laravel-permission/pull/2499\n- `@haspermission` directive by @axlwild in https://github.com/spatie/laravel-permission/pull/2515\n- Add guard parameter to can() by @drbyte in https://github.com/spatie/laravel-permission/pull/2526\n\n### New Contributors\n\n- @xenaio-daniil made their first contribution in https://github.com/spatie/laravel-permission/pull/2280\n- @JensvandeWiel made their first contribution in https://github.com/spatie/laravel-permission/pull/2336\n- @fsamapoor made their first contribution in https://github.com/spatie/laravel-permission/pull/2361\n- @yungifez made their first contribution in https://github.com/spatie/laravel-permission/pull/2394\n- @HasanEksi made their first contribution in https://github.com/spatie/laravel-permission/pull/2418\n- @jnoordsij made their first contribution in https://github.com/spatie/laravel-permission/pull/2424\n- @danharrin made their first contribution in https://github.com/spatie/laravel-permission/pull/2445\n- @SuperDJ made their first contribution in https://github.com/spatie/laravel-permission/pull/2467\n- @ChillMouse made their first contribution in https://github.com/spatie/laravel-permission/pull/2438\n- @Okipa made their first contribution in https://github.com/spatie/laravel-permission/pull/2492\n- @edalzell made their first contribution in https://github.com/spatie/laravel-permission/pull/2494\n- @sirosfakhri made their first contribution in https://github.com/spatie/laravel-permission/pull/2501\n- @juliangums made their first contribution in https://github.com/spatie/laravel-permission/pull/2516\n- @nnnnnnnngu made their first contribution in https://github.com/spatie/laravel-permission/pull/2524\n- @axlwild made their first contribution in https://github.com/spatie/laravel-permission/pull/2515\n- @shdehnavi made their first contribution in https://github.com/spatie/laravel-permission/pull/2527\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.11.1...6.0.0\n\n## 5.11.1 - 2023-10-25\n\nNo functional changes. Just several small updates to the Documentation.\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.11.0...5.11.1\n\n## 5.11.0 - 2023-08-30\n\n### What's Changed\n\n- [V5] Avoid triggering `eloquent.retrieved` event by @erikn69 in https://github.com/spatie/laravel-permission/pull/2490\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.10.2...5.11.0\n\n## 5.10.2 - 2023-07-04\n\n### What's Changed\n\n- Fix Eloquent Strictness on `permission:show` Command by @erikn69 in https://github.com/spatie/laravel-permission/pull/2457\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.10.1...5.10.2\n\n## 5.10.1 - 2023-04-12\n\n### What's Changed\n\n- [V5] Fix artisan command `permission:show` output of roles with underscores by @erikn69 in https://github.com/spatie/laravel-permission/pull/2396\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.10.0...5.10.1\n\n## 5.10.0 - 2023-03-22\n\n### What's Changed\n\n- Fix delete permissions on Permissions Model by @erikn69 in https://github.com/spatie/laravel-permission/pull/2366\n\n## 5.9.1 - 2023-02-06\n\nApologies for the break caused by 5.9.0 !\n\n### Reverted Lazy binding of dependencies.\n\n- Revert \"fix: Lazily bind dependencies\", originally #2309\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.9.0...5.9.1\n\n## 5.9.0 - 2023-02-06\n\n### What's Changed\n\n- Add `permission-` prefix to publish tag names by @sedehi in https://github.com/spatie/laravel-permission/pull/2301\n- Fix detaching user models on teams feature #2220 by @erikn69 in https://github.com/spatie/laravel-permission/pull/2221\n- Hint model properties by @AJenbo in https://github.com/spatie/laravel-permission/pull/2230\n- Custom wildcard verification/separators support by @erikn69 in https://github.com/spatie/laravel-permission/pull/2252\n- fix: Lazily bind dependencies by @olivernybroe in https://github.com/spatie/laravel-permission/pull/2309\n- Extract query to `getPermissionsWithRoles` method. by @xiCO2k in https://github.com/spatie/laravel-permission/pull/2316\n- This will allow to extend the PermissionRegistrar class and change the query.\n\n### New Contributors\n\n- @sedehi made their first contribution in https://github.com/spatie/laravel-permission/pull/2301\n- @parallels999 made their first contribution in https://github.com/spatie/laravel-permission/pull/2265\n- @AJenbo made their first contribution in https://github.com/spatie/laravel-permission/pull/2230\n- @olivernybroe made their first contribution in https://github.com/spatie/laravel-permission/pull/2309\n- @xiCO2k made their first contribution in https://github.com/spatie/laravel-permission/pull/2316\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.8.0...5.9.0\n\n## 5.8.0 - 2023-01-14\n\n### What's Changed\n\n- Laravel 10.x Support by @erikn69 in https://github.com/spatie/laravel-permission/pull/2298\n\n#### Administrative\n\n- [Docs] Link updated to match name change of related tool repo by @aliqasemzadeh in https://github.com/spatie/laravel-permission/pull/2253\n- Fix tests badge by @erikn69 in https://github.com/spatie/laravel-permission/pull/2300\n- Add Laravel Pint Support by @patinthehat in https://github.com/spatie/laravel-permission/pull/2269\n- Normalize composer.json by @patinthehat in https://github.com/spatie/laravel-permission/pull/2259\n- Add Dependabot Automation by @patinthehat in https://github.com/spatie/laravel-permission/pull/2257\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.7.0...5.8.0\n\n## 5.7.0 - 2022-11-23\n\n### What's Changed\n\n- [Bugfix] Avoid checking permissions-via-roles on `Role` model (ref `Model::preventAccessingMissingAttributes()`) by @juliomotol in https://github.com/spatie/laravel-permission/pull/2227\n\n### New Contributors\n\n- @juliomotol made their first contribution in https://github.com/spatie/laravel-permission/pull/2227\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.6.0...5.7.0\n\n## 5.6.0 - 2022-11-19\n\n### What's Changed\n\n- No longer throws an exception when checking `hasAllPermissions()` if the permission name does not exist by @mtawil in https://github.com/spatie/laravel-permission/pull/2248\n\n### Doc Updates\n\n- [Docs] Add syncPermissions() in role-permissions.md by @xorinzor in https://github.com/spatie/laravel-permission/pull/2235\n- [Docs] Fix broken Link that link to freek's blog post by @chengkangzai in https://github.com/spatie/laravel-permission/pull/2234\n\n### New Contributors\n\n- @xorinzor made their first contribution in https://github.com/spatie/laravel-permission/pull/2235\n- @chengkangzai made their first contribution in https://github.com/spatie/laravel-permission/pull/2234\n- @mtawil made their first contribution in https://github.com/spatie/laravel-permission/pull/2248\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.16...5.6.0\n\n## 5.5.16 - 2022-10-23\n\n### What's Changed\n\n- optimize `for` loop in WildcardPermission by @SubhanSh in https://github.com/spatie/laravel-permission/pull/2113\n\n### New Contributors\n\n- @SubhanSh made their first contribution in https://github.com/spatie/laravel-permission/pull/2113\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.15...5.5.16\n\n## 5.5.15 - 2022-10-23\n\nAutocomplete all Blade directives via Laravel Idea plugin\n\n### What's Changed\n\n- Autocomplete all Blade directives via Laravel Idea plugin by @maartenpaauw in https://github.com/spatie/laravel-permission/pull/2210\n- Add tests for display roles/permissions on UnauthorizedException by @erikn69 in https://github.com/spatie/laravel-permission/pull/2228\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.14...5.5.15\n\n## 5.5.14 - 2022-10-21\n\nFIXED BREAKING CHANGE. (Sorry about that!)\n\n### What's Changed\n\n- Revert \"Avoid calling the config helper in the role/perm model constructor\" by @drbyte in https://github.com/spatie/laravel-permission/pull/2225\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.13...5.5.14\n\n## 5.5.13 - 2022-10-21\n\n### What's Changed\n\n- fix UnauthorizedException: Wrong configuration was used in forRoles by @Sy-Dante in https://github.com/spatie/laravel-permission/pull/2224\n\n### New Contributors\n\n- @Sy-Dante made their first contribution in https://github.com/spatie/laravel-permission/pull/2224\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.12...5.5.13\n\n## 5.5.12 - 2022-10-19\n\nFix regression introduced in `5.5.10`\n\n### What's Changed\n\n- Fix undefined index guard_name by @erikn69 in https://github.com/spatie/laravel-permission/pull/2219\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.11...5.5.12\n\n## 5.5.11 - 2022-10-19\n\n### What's Changed\n\n- Support static arrays on blade directives by @erikn69 in https://github.com/spatie/laravel-permission/pull/2168\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.10...5.5.11\n\n## 5.5.10 - 2022-10-19\n\n### What's Changed\n\n- Avoid calling the config helper in the role/perm model constructor by @adiafora in https://github.com/spatie/laravel-permission/pull/2098 as discussed in https://github.com/spatie/laravel-permission/issues/2097 regarding `DI`\n\n### New Contributors\n\n- @adiafora made their first contribution in https://github.com/spatie/laravel-permission/pull/2098\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.9...5.5.10\n\n## 5.5.9 - 2022-10-19\n\nCompatibility Bugfix\n\n### What's Changed\n\n- Prevent `MissingAttributeException` for `guard_name` by @ejunker in https://github.com/spatie/laravel-permission/pull/2216\n\n### New Contributors\n\n- @ejunker made their first contribution in https://github.com/spatie/laravel-permission/pull/2216\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.8...5.5.9\n\n## 5.5.8 - 2022-10-19\n\n`HasRoles` trait\n\n### What's Changed\n\n- Fix returning all roles instead of the assigned by @erikn69 in https://github.com/spatie/laravel-permission/pull/2194\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.7...5.5.8\n\n## 5.5.7 - 2022-10-19\n\nOptimize HasPermissions trait\n\n### What's Changed\n\n- Delegate permission collection filter to another method by @angeljqv in https://github.com/spatie/laravel-permission/pull/2182\n- Delegate permission filter to another method by @angeljqv in https://github.com/spatie/laravel-permission/pull/2183\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.6...5.5.7\n\n## 5.5.6 - 2022-10-19\n\nJust a maintenance release.\n\n### What's Changed\n\n- Actions: add PHP 8.2 Build by @erikn69 in https://github.com/spatie/laravel-permission/pull/2214\n- Docs: Fix small syntax error in teams-permissions.md by @miten5 in https://github.com/spatie/laravel-permission/pull/2171\n- Docs: Update documentation for multiple guards by @gms8994 in https://github.com/spatie/laravel-permission/pull/2169\n- Docs: Make Writing Policies link clickable by @maartenpaauw in https://github.com/spatie/laravel-permission/pull/2202\n- Docs: Add note about non-standard User models by @androidacy-user in https://github.com/spatie/laravel-permission/pull/2179\n- Docs: Fix explanation of results for hasAllDirectPermissions in role-permission.md by @drdan18 in https://github.com/spatie/laravel-permission/pull/2139\n- Docs: Add ULIDs reference by @erikn69 in https://github.com/spatie/laravel-permission/pull/2213\n\n### New Contributors\n\n- @miten5 made their first contribution in https://github.com/spatie/laravel-permission/pull/2171\n- @gms8994 made their first contribution in https://github.com/spatie/laravel-permission/pull/2169\n- @maartenpaauw made their first contribution in https://github.com/spatie/laravel-permission/pull/2202\n- @androidacy-user made their first contribution in https://github.com/spatie/laravel-permission/pull/2179\n- @drdan18 made their first contribution in https://github.com/spatie/laravel-permission/pull/2139\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.5...5.5.6\n\n## 5.5.5 - 2022-06-29\n\n### What's Changed\n\n- Custom primary keys tests(Only tests) by @erikn69 in https://github.com/spatie/laravel-permission/pull/2096\n- [PHP 8.2] Fix `${var}` string interpolation deprecation by @Ayesh in https://github.com/spatie/laravel-permission/pull/2117\n- Use `getKey`, `getKeyName` instead of `id` by @erikn69 in https://github.com/spatie/laravel-permission/pull/2116\n- On WildcardPermission class use static instead of self for extending by @erikn69 in https://github.com/spatie/laravel-permission/pull/2111\n- Clear roles array after hydrate from cache by @angeljqv in https://github.com/spatie/laravel-permission/pull/2099\n\n### New Contributors\n\n- @Ayesh made their first contribution in https://github.com/spatie/laravel-permission/pull/2117\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.4...5.5.5\n\n## 5.5.4 - 2022-05-16\n\n## What's Changed\n\n- Support custom primary key names on models by @erikn69 in https://github.com/spatie/laravel-permission/pull/2092\n- Fix UuidTrait on uuid doc page by @abhishekpaul in https://github.com/spatie/laravel-permission/pull/2094\n- Support custom fields on cache by @erikn69 in https://github.com/spatie/laravel-permission/pull/2091\n\n## New Contributors\n\n- @abhishekpaul made their first contribution in https://github.com/spatie/laravel-permission/pull/2094\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.3...5.5.4\n\n## 5.5.3 - 2022-05-05\n\n## What's Changed\n\n- Update .gitattributes by @angeljqv in https://github.com/spatie/laravel-permission/pull/2065\n- Remove double semicolon from add_teams_fields.php.stub by @morganarnel in https://github.com/spatie/laravel-permission/pull/2067\n- [V5] Allow revokePermissionTo to accept Permission[] by @erikn69 in https://github.com/spatie/laravel-permission/pull/2014\n- [V5] Improve typing in role's findById and findOrCreate method by @itsfaqih in https://github.com/spatie/laravel-permission/pull/2022\n- [V5] Cache loader improvements by @erikn69 in https://github.com/spatie/laravel-permission/pull/1912\n\n## New Contributors\n\n- @morganarnel made their first contribution in https://github.com/spatie/laravel-permission/pull/2067\n- @itsfaqih made their first contribution in https://github.com/spatie/laravel-permission/pull/2022\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.2...5.5.3\n\n## 5.5.2 - 2022-03-09\n\n## What's Changed\n\n- [Fixes BIG bug] register blade directives after resolving blade compiler by @tabacitu in https://github.com/spatie/laravel-permission/pull/2048\n\n## New Contributors\n\n- @tabacitu made their first contribution in https://github.com/spatie/laravel-permission/pull/2048\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.1...5.5.2\n\n## 5.5.1 - 2022-03-03\n\n## What's Changed\n\n- Spelling correction by @gergo85 in https://github.com/spatie/laravel-permission/pull/2024\n- update broken link to laravel exception by @kingzamzon in https://github.com/spatie/laravel-permission/pull/2023\n- Fix Blade Directives incompatibility with renderers by @erikn69 in https://github.com/spatie/laravel-permission/pull/2039\n\n## New Contributors\n\n- @gergo85 made their first contribution in https://github.com/spatie/laravel-permission/pull/2024\n- @kingzamzon made their first contribution in https://github.com/spatie/laravel-permission/pull/2023\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.5.0...5.5.1\n\n## 5.5.0 - 2022-01-11\n\n- add support for Laravel 9\n\n## 5.4.0 - 2021-11-17\n\n## What's Changed\n\n- Add support for PHP 8.1 by @freekmurze in https://github.com/spatie/laravel-permission/pull/1926\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.3.2...5.4.0\n\n## 5.3.2 - 2021-11-17\n\n## What's Changed\n\n- [V5] Support for custom key names on Role,Permission by @erikn69 in https://github.com/spatie/laravel-permission/pull/1913\n\n**Full Changelog**: https://github.com/spatie/laravel-permission/compare/5.3.1...5.3.2\n\n## 5.3.1 - 2021-11-04\n\n- Fix hints, support int on scopePermission (#1908)\n\n## 5.3.0 - 2021-10-29\n\n- Option for custom logic for checking permissions (#1891)\n\n## 5.2.0 - 2021-10-28\n\n- [V5] Fix detaching on all teams instead of only current #1888 by @erikn69 in https://github.com/spatie/laravel-permission/pull/1890\n- [V5] Add uuid compatibility support on teams by @erikn69 in https://github.com/spatie/laravel-permission/pull/1857\n- Adds setRoleClass method to PermissionRegistrar by @timschwartz in https://github.com/spatie/laravel-permission/pull/1867\n- Load permissions for preventLazyLoading by @bahramsadin in https://github.com/spatie/laravel-permission/pull/1884\n- [V5] Doc for `Super Admin` on teams by @erikn69 in https://github.com/spatie/laravel-permission/pull/1845\n\n## 5.1.1 - 2021-09-01\n\n- Avoid Roles over-hydration #1834\n\n## 5.1.0 - 2021-08-31\n\n- No longer flush cache on User role/perm assignment changes #1832\n- NOTE:  You should test your app to be sure that you don't accidentally have deep dependencies on cache resets happening automatically in these cases.\n- ALSO NOTE: If you have added custom code which depended on these flush operations, you may need to add your own cache-reset calls.\n\n## 5.0.0 - 2021-08-31\n\n- Change default-guard-lookup to prefer current user's guard (see BC note in #1817 )\n- Teams/Groups feature (see docs, or PR #1804)\n- Customized pivots instead of `role_id`,`permission_id` #1823\n\n## 4.4.1 - 2021-09-01\n\n- Avoid Roles over-hydration #1834\n\n## 4.4.0 - 2021-08-28\n\n- Avoid BC break (removed interface change) on cache change added in 4.3.0 #1826\n- Made cache even smaller #1826\n- Avoid re-sync on non-persisted objects when firing Eloquent::saved #1819\n\n## 4.3.0 - 2021-08-17\n\n- Speed up permissions cache lookups, and make cache smaller #1799\n\n## 4.2.0 - 2021-06-04\n\n- Add hasExactRoles method #1696\n\n## 4.1.0 - 2021-06-01\n\n- Refactor to resolve guard only once during middleware\n- Refactor service provider by extracting some methods\n\n## 4.0.1 - 2021-03-22\n\n- Added note in migration for field lengths on MySQL 8. (either shorten the columns to 125 or use InnoDB)\n\n## 4.0.0 - 2021-01-27\n\n- Drop support on Laravel 5.8 #1615\n- Fix bug when adding roles to a model that doesn't yet exist #1663\n- Enforce unique constraints on database level #1261\n- Changed PermissionRegistrar::initializeCache() public to allow reinitializing cache in custom situations. #1521\n- Use Eloquent\\Collection instead of Support\\Collection for consistency, collection merging, etc #1630\n\nThis package now requires PHP 7.2.5 and Laravel 6.0 or higher.\nIf you are on a PHP version below 7.2.5 or a Laravel version below 6.0 you can use an older version of this package.\n\n## 3.18.0 - 2020-11-27\n\n- Allow PHP 8.0\n\n## 3.17.0 - 2020-09-16\n\n- Optional `$guard` parameter may be passed to `RoleMiddleware`, `PermissionMiddleware`, and `RoleOrPermissionMiddleware`. See #1565\n\n## 3.16.0 - 2020-08-18\n\n- Added Laravel 8 support\n\n## 3.15.0 - 2020-08-15\n\n- Change `users` relationship type to BelongsToMany\n\n## 3.14.0 - 2020-08-15\n\n- Declare table relations earlier to improve guarded/fillable detection accuracy (relates to Aug 2020 Laravel security patch)\n\n## 3.13.0 - 2020-05-19\n\n- Provide migration error text to stop caching local config when installing packages.\n\n## 3.12.0 - 2020-05-14\n\n- Add missing config setting for `display_role_in_exception`\n- Ensure artisan `permission:show` command uses configured models\n\n## 3.11.0 - 2020-03-03\n\n- Allow guardName() as a function with priority over $guard_name property #1395\n\n## 3.10.1 - 2020-03-03\n\n- Update patch to handle intermittent error in #1370\n\n## 3.10.0 - 2020-03-02\n\n- Ugly patch to handle intermittent error: `Trying to access array offset on value of type null` in #1370\n\n## 3.9.0 - 2020-02-26\n\n- Add Wildcard Permissions feature #1381 (see PR or docs for details)\n\n## 3.8.0 - 2020-02-18\n\n- Clear in-memory permissions on boot, for benefit of long running processes like Swoole. #1378\n\n## 3.7.2 - 2020-02-17\n\n- Refine test for Lumen dependency. Ref #1371, Fixes #1372.\n\n## 3.7.1 - 2020-02-15\n\n- Internal refactoring of scopes to use whereIn instead of orWhere #1334, #1335\n- Internal refactoring to flatten collection on splat #1341\n\n## 3.7.0 - 2020-02-15\n\n- Added methods to check any/all when querying direct permissions #1245\n- Removed older Lumen dependencies #1371\n\n## 3.6.0 - 2020-01-17\n\n- Added Laravel 7.0 support\n- Allow splat operator for passing roles to `hasAnyRole()`\n\n## 3.5.0 - 2020-01-07\n\n- Added missing `guardName` to Exception `PermissionDoesNotExist` #1316\n\n## 3.4.1 - 2019-12-28\n\n- Fix 3.4.0 for Lumen\n\n## 3.4.0 - 2019-12-27\n\n- Make compatible with Swoole - ie: for long-running Laravel instances\n\n## 3.3.1 - 2019-12-24\n\n- Expose Artisan commands to app layer, not just to console\n\n## 3.3.0 - 2019-11-22\n\n- Remove duplicate and unreachable code\n- Remove checks for older Laravel versions\n\n## 3.2.0 - 2019-10-16\n\n- Implementation of optional guard check for hasRoles and hasAllRoles - See #1236\n\n## 3.1.0 - 2019-10-16\n\n- Use bigIncrements/bigInteger in migration - See #1224\n\n## 3.0.0 - 2019-09-02\n\n- Update dependencies to allow for Laravel 6.0\n- Drop support for Laravel 5.7 and older, and PHP 7.1 and older. (They can use v2 of this package until they upgrade.)\n- To be clear: v3 requires minimum Laravel 5.8 and PHP 7.2\n\n## 2.38.0 - 2019-09-02\n\n- Allow support for multiple role/permission models\n- Load roles relationship only when missing\n- Wrap helpers in function_exists() check\n\n## 2.37.0 - 2019-04-09\n\n- Added `permission:show` CLI command to display a table of roles/permissions\n- `removeRole` now returns the model, consistent with other methods\n- model `$guarded` properties updated to `protected`\n- README updates\n\n## 2.36.1 - 2019-03-05\n\n- reverts the changes made in 2.36.0 due to some reported breaks.\n\n## 2.36.0 - 2019-03-04\n\n- improve performance by reducing another iteration in processing query results and returning earlier\n\n## 2.35.0 - 2019-03-01\n\n- overhaul internal caching strategy for better performance and fix cache miss when permission names contained spaces\n- deprecated hasUncachedPermissionTo() (use hasPermissionTo() instead)\n- added getPermissionNames() method\n\n## 2.34.0 - 2019-02-26\n\n- Add explicit pivotKeys to roles/permissions BelongsToMany relationships\n\n## 2.33.0 - 2019-02-20\n\n- Laravel 5.8 compatibility\n\n## 2.32.0 - 2019-02-13\n\n- Fix duplicate permissions being created through artisan command\n\n## 2.31.0 - 2019-02-03\n\n- Add custom guard query to role scope\n- Remove use of array_wrap helper function due to future deprecation\n\n## 2.30.0 - 2019-01-28\n\n- Change cache config time to DateInterval instead of integer\n\nThis is in preparation for compatibility with Laravel 5.8's cache TTL change to seconds instead of minutes.\n\nNOTE: If you leave your existing `config/permission.php` file alone, then with Laravel 5.8 the `60 * 24` will change from being treated as 24 hours to just 24 minutes. Depending on your app, this may or may not make a significant difference.  Updating your config file to a specific DateInterval will add specificity and insulate you from the TTL change in Laravel 5.8.\n\nRefs:\n\nhttps://laravel-news.com/cache-ttl-change-coming-to-laravel-5-8\nhttps://github.com/laravel/framework/commit/fd6eb89b62ec09df1ffbee164831a827e83fa61d\n\n## 2.29.0 - 2018-12-15\n\n- Fix bound `saved` event from firing on all subsequent models when calling assignRole or givePermissionTo on unsaved models. However, it is preferable to save the model first, and then add roles/permissions after saving. See #971.\n\n## 2.28.2 - 2018-12-10\n\n- Use config settings for cache reset in migration stub\n\n## 2.28.1 - 2018-12-07\n\n- Remove use of Cache facade, for Lumen compatibility\n\n## 2.28.0 - 2018-11-30\n\n- Rename `getCacheKey` method in HasPermissions trait to `getPermissionCacheKey` for clearer specificity.\n\n## 2.27.0 - 2018-11-21\n\n- Add ability to specify a cache driver for roles/permissions caching\n\n## 2.26.2 - 2018-11-20\n\n- Added the ability to reset the permissions cache via an Artisan command:\n- `php artisan permission:cache-reset`\n\n## 2.26.1 - 2018-11-19\n\n- minor update to de-duplicate code overhead\n- numerous internal updates to cache tests infrastructure\n\n## 2.26.0 - 2018-11-19\n\n- Substantial speed increase by caching the associations between models and permissions\n\n### NOTES:\n\nThe following changes are not \"breaking\", but worth making the updates to your app for consistency.\n\n1. Config file: The `config/permission.php` file changed to move cache-related settings into a sub-array. **You should review the changes and merge the updates into your own config file.** Specifically the `expiration_time` value has moved into a sub-array entry, and the old top-level entry is no longer used.\n2. See the original config file here:\n3. https://github.com/spatie/laravel-permission/blob/main/config/permission.php\n4. \n5. Cache Resets: If your `app` or `tests` are clearing the cache by specifying the cache key, **it is better to use the built-in forgetCachedPermissions() method** so that it properly handles tagged cache entries. Here is the recommended change:\n6. \n\n```diff\n- app()['cache']->forget('spatie.permission.cache');\n+ $this->app->make(\\Spatie\\Permission\\PermissionRegistrar::class)->forgetCachedPermissions();\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n```\n1. Also this is a good time to point out that now with v2.25.0 and v2.26.0 most permission-cache-reset scenarios may no longer be needed in your app, so it's worth reviewing those cases, as you may gain some app speed improvement by removing unnecessary cache resets.\n\n## 2.25.0 - 2018-11-07\n\n- A model's `roles` and `permissions` relations (respectively) are now automatically reloaded after an Assign/Remove role or Grant/Revoke of permissions. This means there's no longer a need to call `-&amp;gt;fresh()` on the model if the only reason is to reload the role/permission relations. (That said, you may want to call it for other reasons.)\n- Added support for passing id to HasRole()\n\n## 2.24.0 - 2018-11-06\n\n- Fix operator used on RoleOrPermissionMiddleware, and avoid throwing PermissionDoesNotExist if invalid permission passed\n- Auto-reload model role relation after using AssignRole\n- Avoid empty permission creation when using the CreateRole command\n\n## 2.23.0 - 2018-10-15\n\n- Avoid unnecessary queries of user roles when fetching all permissions\n\n## 2.22.1 - 2018-10-15\n\n- Fix Lumen issue with Route helper added in 2.22.0\n\n## 2.22.0 - 2018-10-11\n\n- Added `Route::role()` and `Route::permission()` middleware helper functions\n- Added new `role_or_permission` middleware to allow specifying \"or\" combinations\n\n## 2.21.0 - 2018-09-29\n\n- Revert changes from 2.17.1 in order to support Lumen 5.7\n\n## 2.20.0 - 2018-09-19\n\n- It will sync roles/permissions to models that are not persisted, by registering a `saved` callback.\n- (It would previously throw an Integrity constraint violation QueryException on the pivot table insertion.)\n\n## 2.19.2 - 2018-09-19\n\n- add `@elserole` directive:\n- Usage:\n\n```php\n@role('roleA')\n // user hasRole 'roleA'\n@elserole('roleB')\n // user hasRole 'roleB' but not 'roleA'\n@endrole\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n```\n## 2.19.1 - 2018-09-14\n\n- Spark-related fix to accommodate missing guard[providers] config\n\n## 2.19.0 - 2018-09-10\n\n- Add ability to pass in IDs or mixed values to `role` scope\n- Add `@unlessrole`/`@endunlessrole` Blade directives\n\n## 2.18.0 - 2018-09-06\n\n- Expanded CLI `permission:create-role` command to create optionally create-and-link permissions in one command. Also now no longer throws an error if the role already exists.\n\n## 2.17.1 - 2018-08-28\n\n- Require laravel/framework instead of illuminate/* starting from ~5.4.0\n- Removed old dependency for illuminate/database@~5.3.0 (Laravel 5.3 is not supported)\n\n## 2.17.0 - 2018-08-24\n\n- Laravel 5.7 compatibility\n\n## 2.16.0 - 2018-08-20\n\n- Replace static Permission::class and Role::class with dynamic value (allows custom models more easily)\n- Added type checking in hasPermissionTo and hasDirectPermission\n\n## 2.15.0 - 2018-08-15\n\n- Make assigning the same role or permission twice not throw an exception\n\n## 2.14.0 - 2018-08-13\n\n- Allow using another key name than `model_id` by defining new `columns` array with `model_morph_key` key in config file. This improves UUID compatibility as discussed in #777.\n\n## 2.13.0 - 2018-08-02\n\n- Fix issue with null values passed to syncPermissions & syncRoles\n\n## 2.12.2 - 2018-06-13\n\n- added hasAllPermissions method\n\n## 2.12.1 - 2018-04-23\n\n- Reverted 2.12.0. REVERTS: \"Add ability to pass guard name to gate methods like can()\". Requires reworking of guard handling if we're going to add this feature.\n\n## 2.12.0 - 2018-04-22\n\n- Add ability to pass guard name to gate methods like can()\n\n## 2.11.0 - 2018-04-16\n\n- Improve speed of permission lookups with findByName, findById, findOrCreate\n\n## 2.10.0 - 2018-04-15\n\n- changes the type-hinted Authenticatable to Authorizable in the PermissionRegistrar.\n- (Previously it was expecting models to implement the Authenticatable contract; but really that should have been Authorizable, since that's where the Gate functionality really is.)\n\n## 2.9.2 - 2018-03-12\n\n- Now findOrCreate() exists for both Roles and Permissions\n- Internal code refactoring for future dev work\n\n## 2.9.1 - 2018-02-23\n\n- Permissions now support passing integer id for sync, find, hasPermissionTo and hasDirectPermissionTo\n\n## 2.9.0 - 2018-02-07\n\n- add compatibility with Laravel 5.6\n- Allow assign/sync/remove Roles from Permission model\n\n## 2.8.2 - 2018-02-07\n\n- Allow a collection containing a model to be passed to role/permission scopes\n\n## 2.8.1 - 2018-02-03\n\n- Fix compatibility with Spark v2.0 to v5.0\n\n## 2.8.0 - 2018-01-25\n\n- Support getting guard_name from extended model when using static methods\n\n## 2.7.9 - 2018-01-23\n\nChanges related to throwing UnauthorizedException:\n\n- When UnauthorizedException is thrown, a property is added with the expected role/permission which triggered it\n- A configuration option may be set to include the list of required roles/permissions in the message\n\n## 2.7.8 - 2018-01-02\n\n- REVERTED: Dynamic permission_id and role_id columns according to tables name\n- NOTE: This Dynamic field naming was a breaking change, so we've removed it for now.\n\nBEST NOT TO USE v2.7.7 if you've changed tablenames in the config file.\n\n## 2.7.7 - 2017-12-31\n\n- updated `HasPermissions::getStoredPermission` to allow a collection to be returned, and to fix query when passing multiple permissions\n- Give and revoke multiple permissions\n- Dynamic permission_id and role_id columns according to tables name\n- Add findOrCreate function to Permission model\n- Improved Lumen support\n- Allow guard name to be null for find role by id\n\n## 2.7.6 - 2017-11-27\n\n- added Lumen support\n- updated `HasRole::assignRole` and `HasRole::syncRoles` to accept role id's in addition to role names as arguments\n\n## 2.7.5 - 2017-10-26\n\n- fixed `Gate::before` for custom gate callbacks\n\n## 2.7.4 - 2017-10-26\n\n- added cache clearing command in `up` migration for permission tables\n- use config_path helper for better Lumen support\n\n## 2.7.3 - 2017-10-21\n\n- refactor middleware to throw custom `UnauthorizedException` (which raises an HttpException with 403 response)\n- The 403 response is backward compatible\n\n## 2.7.2 - 2017-10-18\n\n- refactor `PermissionRegistrar` to use `$gate-&amp;gt;before()`\n- removed `log_registration_exception` as it is no longer relevant\n\n## 2.7.1 - 2017-10-12\n\n- fixed a bug where `Role`s and `Permission`s got detached when soft deleting a model\n\n## 2.7.0 - 2017-09-27\n\n- add support for L5.3\n\n## 2.6.0 - 2017-09-10\n\n- add `permission` scope\n\n## 2.5.4 - 2017-09-07\n\n- register the blade directives in the register method of the service provider\n\n## 2.5.3 - 2017-09-07\n\n- register the blade directives in the boot method of the service provider\n\n## 2.5.2 - 2017-09-05\n\n- let middleware use caching\n\n## 2.5.1 - 2017-09-02\n\n- add getRoleNames() method to return a collection of assigned roles\n\n## 2.5.0 - 2017-08-30\n\n- add compatibility with Laravel 5.5\n\n## 2.4.2 - 2017-08-11\n\n- automatically detach roles and permissions when a user gets deleted\n\n## 2.4.1 - 2017-08-05\n\n- fix processing of pipe symbols in `@hasanyrole` and `@hasallroles` Blade directives\n\n## 2.4.0 -2017-08-05\n\n- add `PermissionMiddleware` and `RoleMiddleware`\n\n## 2.3.2 - 2017-07-28\n\n- allow `hasAnyPermission` to take an array of permissions\n\n## 2.3.1 - 2017-07-27\n\n- fix commands not using custom models\n\n## 2.3.0 - 2017-07-25\n\n- add `create-permission` and `create-role` commands\n\n## 2.2.0 - 2017-07-01\n\n- `hasanyrole` and `hasallrole` can accept multiple roles\n\n## 2.1.6 - 2017-06-06\n\n- fixed a bug where `hasPermissionTo` wouldn't use the right guard name\n\n## 2.1.5 - 2017-05-17\n\n- fixed a bug that didn't allow you to assign a role or permission when using multiple guards\n\n## 2.1.4 - 2017-05-10\n\n- add `model_type` to the primary key of tables that use a polymorphic relationship\n\n## 2.1.3 - 2017-04-21\n\n- fixed a bug where the role()/permission() relation to user models would be saved incorrectly\n- added users() relation on Permission and Role\n\n## 2.1.2 - 2017-04-20\n\n- fix a bug where the `role()`/`permission()` relation to user models would be saved incorrectly\n- add `users()` relation on `Permission` and `Role`\n\n## 2.0.2 - 2017-04-13\n\n- check for duplicates when adding new roles and permissions\n\n## 2.0.1 - 2017-04-11\n\n- fix the order of the `foreignKey` and `relatedKey` in the relations\n\n## 2.0.0 - 2017-04-10\n\n- Requires minimum Laravel 5.4\n- cache expiration is now configurable and set to one day by default\n- roles and permissions can now be assigned to any model through the `HasRoles` trait\n- removed deprecated `hasPermission` method\n- renamed config file from `laravel-permission` to `permission`.\n\n## 1.17.0 - 2018-08-24\n\n- added support for Laravel 5.7\n\n## 1.16.0 - 2018-02-07\n\n- added support for Laravel 5.6\n\n## 1.15 - 2017-12-08\n\n- allow `hasAnyPermission` to take an array of permissions\n\n## 1.14.1 - 2017-10-26\n\n- fixed `Gate::before` for custom gate callbacks\n\n## 1.14.0 - 2017-10-18\n\n- refactor `PermissionRegistrar` to use `$gate-&amp;gt;before()`\n- removed `log_registration_exception` as it is no longer relevant\n\n## 1.13.0 - 2017-08-31\n\n- added compatibility for Laravel 5.5\n\n## 1.12.0\n\n- made foreign key name to users table configurable\n\n## 1.11.1\n\n- `hasPermissionTo` uses the cache to avoid extra queries when it is called multiple times\n\n## 1.11.0\n\n- add `getDirectPermissions`, `getPermissionsViaRoles`, `getAllPermissions`\n\n## 1.10.0 - 2017-02-22\n\n- add `hasAnyPermission`\n\n## 1.9.0 - 2017-02-20\n\n- add `log_registration_exception` in settings file\n- fix for ambiguous column name `id` when using the role scope\n\n## 1.8.0 - 2017-02-09\n\n- `hasDirectPermission` method is now public\n\n## 1.7.0 - 2016-01-23\n\n- added support for Laravel 5.4\n\n## 1.6.1 - 2016-01-19\n\n- make exception logging more verbose\n\n## 1.6.0 - 2016-12-27\n\n- added `Role` scope\n\n## 1.5.3 - 2016-12-15\n\n- moved some things to `boot` method in SP to solve some compatibility problems with other packages\n\n## 1.5.2 - 2016-08-26\n\n- make compatible with L5.3\n\n## 1.5.1 - 2016-07-23\n\n- fixes `givePermissionTo` and `assignRole` in Laravel 5.1\n\n## 1.5.0 - 2016-07-23\n\n** this version does not work in Laravel 5.1, please upgrade to version 1.5.1 of this package\n\n- allowed `givePermissionTo` to accept multiple permissions\n- allowed `assignRole` to accept multiple roles\n- added `syncPermissions`-method\n- added `syncRoles`-method\n- dropped support for PHP 5.5 and HHVM\n\n## 1.4.0 - 2016-05-08\n\n- added `hasPermissionTo` function to the `Role` model\n\n## 1.3.4 - 2016-02-27\n\n- `hasAnyRole` can now properly process an array\n\n## 1.3.3 - 2016-02-24\n\n- `hasDirectPermission` can now accept a string\n\n## 1.3.2 - 2016-02-23\n\n- fixed user table configuration\n\n## 1.3.1 - 2016-01-10\n\n- fixed bug when testing for non existing permissions\n\n## 1.3.0 - 2015-12-25\n\n- added compatibility for Laravel 5.2\n\n## 1.2.1 - 2015-12-22\n\n- use database_path to publish migrations\n\n## 1.2.0 - 2015-10-28\n\n###Added\n\n- support for custom models\n\n## 1.1.0 - 2015-10-12\n\n### Added\n\n- Blade directives\n- `hasAllRoles()`- and `hasAnyRole()`-functions\n\n## 1.0.2 - 2015-10-11\n\n### Fixed\n\n- Fix for running phpunit locally\n\n## 1.0.1 - 2015-09-30\n\n### Fixed\n\n- Fixed the inconsistent naming of the `hasPermission`-method.\n\n## 1.0.0 - 2015-09-16\n\n### Added\n\n- Everything, initial release\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) Spatie bvba <info@spatie.be>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"left\">\n    <a href=\"https://spatie.be/open-source?utm_source=github&utm_medium=banner&utm_campaign=laravel-permission\">\n      <picture>\n        <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://spatie.be/packages/header/laravel-permission/html/dark.webp\">\n        <img alt=\"Logo for laravel-permission\" src=\"https://spatie.be/packages/header/laravel-permission/html/light.webp\">\n      </picture>\n    </a>\n\n<h1>Associate users with permissions and roles</h1>\n\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/laravel-permission.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-permission)\n[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/spatie/laravel-permission/run-tests.yml?branch=main&label=Tests)](https://github.com/spatie/laravel-permission/actions?query=workflow%3ATests+branch%3Amain)\n[![Total Downloads](https://img.shields.io/packagist/dt/spatie/laravel-permission.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-permission)\n    \n</div>\n\n## Documentation, Installation, and Usage Instructions\n\nSee the [documentation](https://spatie.be/docs/laravel-permission/) for detailed installation and usage instructions.\n\n## What It Does\nThis package allows you to manage user permissions and roles in a database.\n\nOnce installed you can do stuff like this:\n\n```php\n// Adding permissions to a user\n$user->givePermissionTo('edit articles');\n\n// Adding permissions via a role\n$user->assignRole('writer');\n\n$role->givePermissionTo('edit articles');\n```\n\nBecause all permissions will be registered on [Laravel's gate](https://laravel.com/docs/authorization), you can check if a user has a permission with Laravel's default `can` function:\n\n```php\n$user->can('edit articles');\n```\n\n## Support us\n\n[<img src=\"https://github-ads.s3.eu-central-1.amazonaws.com/laravel-permission.jpg?t=1\" width=\"419px\" />](https://spatie.be/github-ad-click/laravel-permission)\n\nWe invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).\n\nWe highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).\n\n## Changelog\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.\n\n## Contributing\n\nPlease see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.\n\n### Testing\n\n``` bash\ncomposer test\n```\n\n### Security\n\nIf you discover any security-related issues, please email [security@spatie.be](mailto:security@spatie.be) instead of using the issue tracker.\n\n## Postcardware\n\nYou're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.\n\nOur address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.\n\nWe publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards).\n\n## Credits\n\n- [Chris Brown](https://github.com/drbyte)\n- [Freek Van der Herten](https://github.com/freekmurze)\n- [All Contributors](../../contributors)\n\nThis package is heavily based on [Jeffrey Way](https://twitter.com/jeffrey_way)'s awesome [Laracasts](https://laracasts.com) lessons\non [permissions and roles](https://laracasts.com/series/whats-new-in-laravel-5-1/episodes/16). His original code\ncan be found [in this repo on GitHub](https://github.com/laracasts/laravel-5-roles-and-permissions-demo).\n\nSpecial thanks to [Alex Vanderbist](https://github.com/AlexVanderbist) who greatly helped with `v2`, and to [Chris Brown](https://github.com/drbyte) for his longtime support  helping us maintain the package.\n\nSpecial thanks to [Caneco](https://twitter.com/caneco) for the original logo.\n\n## Alternatives\n\n- [Povilas Korop](https://twitter.com/@povilaskorop) did an excellent job listing the alternatives [in an article on Laravel News](https://laravel-news.com/two-best-roles-permissions-packages). In that same article, he compares laravel-permission to [Joseph Silber](https://github.com/JosephSilber)'s [Bouncer]((https://github.com/JosephSilber/bouncer)), which in our book is also an excellent package.\n- [santigarcor/laratrust](https://github.com/santigarcor/laratrust) implements team support\n- [ultraware/roles](https://github.com/ultraware/roles) (archived) takes a slightly different approach to its features.\n- [zizaco/entrust](https://github.com/zizaco/entrust) offers some wildcard pattern matching\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n"
  },
  {
    "path": "art/README.md",
    "content": "<p align=\"center\">\n    <img src=\"/art/socialcard.png\" width=\"1280\" title=\"Social Card of Laravel Permission\">\n</p>\n\n# Laravel Permission Art\n\nThe logo was inspired by the [Spatie](https://spatie.be) brand, and the well known minimal design of Laravel packages.\n\n## Fonts\n\nThe logo is using the following fonts:\n\n- [Inter 500](https://fonts.google.com/specimen/Inter#500)\n- [Inter 600](https://fonts.google.com/specimen/Inter#600)\n\n## Colors\n\n|                            |#hex     |rgb()             |\n|---                         |---      |---               |\n|![100](/art/palette/100.png)|`#E8F1F4`|`rgb(232,241,244)`|\n|![200](/art/palette/200.png)|`#C6DDE4`|`rgb(198,221,228)`|\n|![300](/art/palette/300.png)|`#A3C8D4`|`rgb(163,200,212)`|\n|![400](/art/palette/400.png)|`#5E9EB3`|`rgb(94,158,179)` |\n|![500](/art/palette/500.png)|`#197593`|`rgb(25,117,147)` |\n|![600](/art/palette/600.png)|`#176984`|`rgb(23,105,132)` |\n|![700](/art/palette/700.png)|`#0F4658`|`rgb(15,70,88)`   |\n|![800](/art/palette/800.png)|`#0B3542`|`rgb(11,53,66)`   |\n|![900](/art/palette/900.png)|`#08232C`|`rgb(8,35,44)`    |\n\n## Requirements\n\n- A screen or a printer\n\n## Installation\n\n- Open the file\n- *Right-click* on the image\n- Choose **\"Save image as…\"** option\n\n## Maintainers\n\n**Laravel Permission** logo is designed and maintained by [Caneco](https://twitter.com/caneco).\n\n## License\n\nAll rights reserved, but with the following extra conditions:\n\n- It is **OK** to use the Laravel Permission logo in the following cases:\n    - In marketing materials for technical events, e.g. meetups, hackathons, conferences and workshops that are related to Laravel.\n    - In open source projects related to Laravel.\n    - In technical articles/videos/books/papers for educational purposes.\n    - To illustrate a commercial product.\n\n- It is **NOT OK** to use the Laravel Permission logo in the following cases without prior written consent from the copyright owners:\n    - Using the Laravel Permission logo in a commercial product for purposes other than illustrating its integration.\n    - Sell physical products that uses the Laravel Permission logo or its variants, e.g. t-shirts.\n\nBy any means the owner reserves the right of final explanation for any use case not explicitly stated above.\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"spatie/laravel-permission\",\n    \"description\": \"Permission handling for Laravel 12 and up\",\n    \"license\": \"MIT\",\n    \"keywords\": [\n        \"spatie\",\n        \"laravel\",\n        \"permission\",\n        \"permissions\",\n        \"roles\",\n        \"acl\",\n        \"rbac\",\n        \"security\"\n    ],\n    \"authors\": [\n        {\n            \"name\": \"Freek Van der Herten\",\n            \"email\": \"freek@spatie.be\",\n            \"homepage\": \"https://spatie.be\",\n            \"role\": \"Developer\"\n        }\n    ],\n    \"homepage\": \"https://github.com/spatie/laravel-permission\",\n    \"require\": {\n        \"php\": \"^8.4\",\n        \"illuminate/auth\": \"^12.0|^13.0\",\n        \"illuminate/container\": \"^12.0|^13.0\",\n        \"illuminate/contracts\": \"^12.0|^13.0\",\n        \"illuminate/database\": \"^12.0|^13.0\",\n        \"spatie/laravel-package-tools\": \"^1.0\"\n    },\n    \"require-dev\": {\n        \"larastan/larastan\": \"^3.9\",\n        \"laravel/passport\": \"^13.0\",\n        \"laravel/pint\": \"^1.0\",\n        \"orchestra/testbench\": \"^10.0|^11.0\",\n        \"pestphp/pest\": \"^3.0|^4.0\",\n        \"pestphp/pest-plugin-laravel\": \"^3.0|^4.1\",\n        \"phpstan/phpstan\": \"^2.1\"\n    },\n    \"minimum-stability\": \"dev\",\n    \"prefer-stable\": true,\n    \"autoload\": {\n        \"psr-4\": {\n            \"Spatie\\\\Permission\\\\\": \"src\"\n        },\n        \"files\": [\n            \"src/helpers.php\"\n        ]\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Spatie\\\\Permission\\\\Tests\\\\\": \"tests\"\n        }\n    },\n    \"config\": {\n        \"sort-packages\": true,\n        \"allow-plugins\": {\n            \"pestphp/pest-plugin\": true\n        }\n    },\n    \"extra\": {\n        \"branch-alias\": {\n            \"dev-main\": \"7.x-dev\",\n            \"dev-master\": \"7.x-dev\"\n        },\n        \"laravel\": {\n            \"providers\": [\n                \"Spatie\\\\Permission\\\\PermissionServiceProvider\"\n            ]\n        }\n    },\n    \"scripts\": {\n        \"test\": \"pest\",\n        \"format\": \"pint\",\n        \"analyse\": \"echo 'Checking dependencies...' && composer require --dev larastan/larastan && phpstan analyse\"\n    }\n}\n"
  },
  {
    "path": "config/permission.php",
    "content": "<?php\n\nreturn [\n\n    'models' => [\n\n        /*\n         * When using the \"HasPermissions\" trait from this package, we need to know which\n         * Eloquent model should be used to retrieve your permissions. Of course, it\n         * is often just the \"Permission\" model but you may use whatever you like.\n         *\n         * The model you want to use as a Permission model needs to implement the\n         * `Spatie\\Permission\\Contracts\\Permission` contract.\n         */\n\n        'permission' => Spatie\\Permission\\Models\\Permission::class,\n\n        /*\n         * When using the \"HasRoles\" trait from this package, we need to know which\n         * Eloquent model should be used to retrieve your roles. Of course, it\n         * is often just the \"Role\" model but you may use whatever you like.\n         *\n         * The model you want to use as a Role model needs to implement the\n         * `Spatie\\Permission\\Contracts\\Role` contract.\n         */\n\n        'role' => Spatie\\Permission\\Models\\Role::class,\n\n    ],\n\n    'table_names' => [\n\n        /*\n         * When using the \"HasRoles\" trait from this package, we need to know which\n         * table should be used to retrieve your roles. We have chosen a basic\n         * default value but you may easily change it to any table you like.\n         */\n\n        'roles' => 'roles',\n\n        /*\n         * When using the \"HasPermissions\" trait from this package, we need to know which\n         * table should be used to retrieve your permissions. We have chosen a basic\n         * default value but you may easily change it to any table you like.\n         */\n\n        'permissions' => 'permissions',\n\n        /*\n         * When using the \"HasPermissions\" trait from this package, we need to know which\n         * table should be used to retrieve your models permissions. We have chosen a\n         * basic default value but you may easily change it to any table you like.\n         */\n\n        'model_has_permissions' => 'model_has_permissions',\n\n        /*\n         * When using the \"HasRoles\" trait from this package, we need to know which\n         * table should be used to retrieve your models roles. We have chosen a\n         * basic default value but you may easily change it to any table you like.\n         */\n\n        'model_has_roles' => 'model_has_roles',\n\n        /*\n         * When using the \"HasRoles\" trait from this package, we need to know which\n         * table should be used to retrieve your roles permissions. We have chosen a\n         * basic default value but you may easily change it to any table you like.\n         */\n\n        'role_has_permissions' => 'role_has_permissions',\n    ],\n\n    'column_names' => [\n        /*\n         * Change this if you want to name the related pivots other than defaults\n         */\n        'role_pivot_key' => null, // default 'role_id',\n        'permission_pivot_key' => null, // default 'permission_id',\n\n        /*\n         * Change this if you want to name the related model primary key other than\n         * `model_id`.\n         *\n         * For example, this would be nice if your primary keys are all UUIDs. In\n         * that case, name this `model_uuid`.\n         */\n\n        'model_morph_key' => 'model_id',\n\n        /*\n         * Change this if you want to use the teams feature and your related model's\n         * foreign key is other than `team_id`.\n         */\n\n        'team_foreign_key' => 'team_id',\n    ],\n\n    /*\n     * When set to true, the method for checking permissions will be registered on the gate.\n     * Set this to false if you want to implement custom logic for checking permissions.\n     */\n\n    'register_permission_check_method' => true,\n\n    /*\n     * When set to true, Laravel\\Octane\\Events\\OperationTerminated event listener will be registered\n     * this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated\n     * NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it.\n     */\n    'register_octane_reset_listener' => false,\n\n    /*\n     * Events will fire when a role or permission is assigned/unassigned:\n     * \\Spatie\\Permission\\Events\\RoleAttachedEvent\n     * \\Spatie\\Permission\\Events\\RoleDetachedEvent\n     * \\Spatie\\Permission\\Events\\PermissionAttachedEvent\n     * \\Spatie\\Permission\\Events\\PermissionDetachedEvent\n     *\n     * To enable, set to true, and then create listeners to watch these events.\n     */\n    'events_enabled' => false,\n\n    /*\n     * Teams Feature.\n     * When set to true the package implements teams using the 'team_foreign_key'.\n     * If you want the migrations to register the 'team_foreign_key', you must\n     * set this to true before doing the migration.\n     * If you already did the migration then you must make a new migration to also\n     * add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions'\n     * (view the latest version of this package's migration file)\n     */\n\n    'teams' => false,\n\n    /*\n     * The class to use to resolve the permissions team id\n     */\n    'team_resolver' => \\Spatie\\Permission\\DefaultTeamResolver::class,\n\n    /*\n     * Passport Client Credentials Grant\n     * When set to true the package will use Passports Client to check permissions\n     */\n\n    'use_passport_client_credentials' => false,\n\n    /*\n     * When set to true, the required permission names are added to exception messages.\n     * This could be considered an information leak in some contexts, so the default\n     * setting is false here for optimum safety.\n     */\n\n    'display_permission_in_exception' => false,\n\n    /*\n     * When set to true, the required role names are added to exception messages.\n     * This could be considered an information leak in some contexts, so the default\n     * setting is false here for optimum safety.\n     */\n\n    'display_role_in_exception' => false,\n\n    /*\n     * By default wildcard permission lookups are disabled.\n     * See documentation to understand supported syntax.\n     */\n\n    'enable_wildcard_permission' => false,\n\n    /*\n     * The class to use for interpreting wildcard permissions.\n     * If you need to modify delimiters, override the class and specify its name here.\n     */\n    // 'wildcard_permission' => Spatie\\Permission\\WildcardPermission::class,\n\n    /* Cache-specific settings */\n\n    'cache' => [\n\n        /*\n         * By default all permissions are cached for 24 hours to speed up performance.\n         * When permissions or roles are updated the cache is flushed automatically.\n         */\n\n        'expiration_time' => \\DateInterval::createFromDateString('24 hours'),\n\n        /*\n         * The cache key used to store all permissions.\n         */\n\n        'key' => 'spatie.permission.cache',\n\n        /*\n         * You may optionally indicate a specific cache driver to use for permission and\n         * role caching using any of the `store` drivers listed in the cache.php config\n         * file. Using 'default' here means to use the `default` set in cache.php.\n         */\n\n        'store' => 'default',\n    ],\n];\n"
  },
  {
    "path": "database/migrations/add_teams_fields.php.stub",
    "content": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    /**\n     * Run the migrations.\n     */\n    public function up(): void\n    {\n        $teams = config('permission.teams');\n        $tableNames = config('permission.table_names');\n        $columnNames = config('permission.column_names');\n        $pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';\n        $pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';\n\n        if (! $teams) {\n            return;\n        }\n\n        throw_if(empty($tableNames), 'Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');\n        throw_if(empty($columnNames['team_foreign_key'] ?? null), 'Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');\n\n        if (! Schema::hasColumn($tableNames['roles'], $columnNames['team_foreign_key'])) {\n            Schema::table($tableNames['roles'], function (Blueprint $table) use ($columnNames) {\n                $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable()->after('id');\n                $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');\n\n                $table->dropUnique('roles_name_guard_name_unique');\n                $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);\n            });\n        }\n\n        if (! Schema::hasColumn($tableNames['model_has_permissions'], $columnNames['team_foreign_key'])) {\n            Schema::table($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission) {\n                $table->unsignedBigInteger($columnNames['team_foreign_key'])->default('1');\n                $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');\n\n                if (DB::getDriverName() !== 'sqlite') {\n                    $table->dropForeign([$pivotPermission]);\n                }\n                $table->dropPrimary();\n\n                $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],\n                    'model_has_permissions_permission_model_type_primary');\n                if (DB::getDriverName() !== 'sqlite') {\n                    $table->foreign($pivotPermission)\n                        ->references('id')\n                        ->on($tableNames['permissions'])\n                        ->cascadeOnDelete();\n                }\n            });\n        }\n\n        if (! Schema::hasColumn($tableNames['model_has_roles'], $columnNames['team_foreign_key'])) {\n            Schema::table($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole) {\n                $table->unsignedBigInteger($columnNames['team_foreign_key'])->default('1');\n                $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');\n\n                if (DB::getDriverName() !== 'sqlite') {\n                    $table->dropForeign([$pivotRole]);\n                }\n                $table->dropPrimary();\n\n                $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],\n                    'model_has_roles_role_model_type_primary');\n                if (DB::getDriverName() !== 'sqlite') {\n                    $table->foreign($pivotRole)\n                        ->references('id')\n                        ->on($tableNames['roles'])\n                        ->cascadeOnDelete();\n                }\n            });\n        }\n\n        app('cache')\n            ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)\n            ->forget(config('permission.cache.key'));\n    }\n\n    /**\n     * Reverse the migrations.\n     */\n    public function down(): void {}\n};\n"
  },
  {
    "path": "database/migrations/create_permission_tables.php.stub",
    "content": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    /**\n     * Run the migrations.\n     */\n    public function up(): void\n    {\n        $teams = config('permission.teams');\n        $tableNames = config('permission.table_names');\n        $columnNames = config('permission.column_names');\n        $pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';\n        $pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';\n\n        throw_if(empty($tableNames), 'Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');\n        throw_if($teams && empty($columnNames['team_foreign_key'] ?? null), 'Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');\n\n        /**\n         * See `docs/prerequisites.md` for suggested lengths on 'name' and 'guard_name' if \"1071 Specified key was too long\" errors are encountered.\n         */\n        Schema::create($tableNames['permissions'], static function (Blueprint $table) {\n            $table->id(); // permission id\n            $table->string('name');\n            $table->string('guard_name');\n            $table->timestamps();\n\n            $table->unique(['name', 'guard_name']);\n        });\n\n        /**\n         * See `docs/prerequisites.md` for suggested lengths on 'name' and 'guard_name' if \"1071 Specified key was too long\" errors are encountered.\n         */\n        Schema::create($tableNames['roles'], static function (Blueprint $table) use ($teams, $columnNames) {\n            $table->id(); // role id\n            if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing\n                $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();\n                $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');\n            }\n            $table->string('name');\n            $table->string('guard_name');\n            $table->timestamps();\n            if ($teams || config('permission.testing')) {\n                $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);\n            } else {\n                $table->unique(['name', 'guard_name']);\n            }\n        });\n\n        Schema::create($tableNames['model_has_permissions'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) {\n            $table->unsignedBigInteger($pivotPermission);\n\n            $table->string('model_type');\n            $table->unsignedBigInteger($columnNames['model_morph_key']);\n            $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');\n\n            $table->foreign($pivotPermission)\n                ->references('id') // permission id\n                ->on($tableNames['permissions'])\n                ->cascadeOnDelete();\n            if ($teams) {\n                $table->unsignedBigInteger($columnNames['team_foreign_key']);\n                $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');\n\n                $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],\n                    'model_has_permissions_permission_model_type_primary');\n            } else {\n                $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'],\n                    'model_has_permissions_permission_model_type_primary');\n            }\n        });\n\n        Schema::create($tableNames['model_has_roles'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) {\n            $table->unsignedBigInteger($pivotRole);\n\n            $table->string('model_type');\n            $table->unsignedBigInteger($columnNames['model_morph_key']);\n            $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');\n\n            $table->foreign($pivotRole)\n                ->references('id') // role id\n                ->on($tableNames['roles'])\n                ->cascadeOnDelete();\n            if ($teams) {\n                $table->unsignedBigInteger($columnNames['team_foreign_key']);\n                $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');\n\n                $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],\n                    'model_has_roles_role_model_type_primary');\n            } else {\n                $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'],\n                    'model_has_roles_role_model_type_primary');\n            }\n        });\n\n        Schema::create($tableNames['role_has_permissions'], static function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) {\n            $table->unsignedBigInteger($pivotPermission);\n            $table->unsignedBigInteger($pivotRole);\n\n            $table->foreign($pivotPermission)\n                ->references('id') // permission id\n                ->on($tableNames['permissions'])\n                ->cascadeOnDelete();\n\n            $table->foreign($pivotRole)\n                ->references('id') // role id\n                ->on($tableNames['roles'])\n                ->cascadeOnDelete();\n\n            $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');\n        });\n\n        app('cache')\n            ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)\n            ->forget(config('permission.cache.key'));\n    }\n\n    /**\n     * Reverse the migrations.\n     */\n    public function down(): void\n    {\n        $tableNames = config('permission.table_names');\n\n        throw_if(empty($tableNames), 'Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');\n\n        Schema::dropIfExists($tableNames['role_has_permissions']);\n        Schema::dropIfExists($tableNames['model_has_roles']);\n        Schema::dropIfExists($tableNames['model_has_permissions']);\n        Schema::dropIfExists($tableNames['roles']);\n        Schema::dropIfExists($tableNames['permissions']);\n    }\n};\n"
  },
  {
    "path": "docs/_index.md",
    "content": "---\ntitle: v7\nslogan: Associate users with roles and permissions\ngithubUrl: https://github.com/spatie/laravel-permission\nbranch: main\n---\n"
  },
  {
    "path": "docs/about-us.md",
    "content": "---\ntitle: About us\n---\n\n[Spatie](https://spatie.be) is a webdesign agency based in Antwerp, Belgium.\n\nOpen source software is used in all projects we deliver. Laravel, Nginx, Ubuntu are just a few \nof the free pieces of software we use every single day. For this, we are very grateful. \nWhen we feel we have solved a problem in a way that can help other developers, \nwe release our code as open source software [on GitHub](https://spatie.be/opensource).\n\nThis package is heavily based on [Jeffrey Way](https://twitter.com/jeffrey_way)'s awesome [Laracasts](https://laracasts.com) lessons\non [permissions and roles](https://laracasts.com/series/whats-new-in-laravel-5-1/episodes/16). His original code\ncan be found [in this repo on GitHub](https://github.com/laracasts/laravel-5-roles-and-permissions-demo). And newer lessons on [mastering permissions in Laravel](https://laracasts.com/series/mastering-permissions-in-laravel) talk about other ways to implement some of these features as well.\n\nSpecial thanks to [Alex Vanderbist](https://github.com/AlexVanderbist) who greatly helped with `v2`, and to [Chris Brown](https://github.com/drbyte) for his longtime support helping us maintain the package.\n"
  },
  {
    "path": "docs/advanced-usage/_index.md",
    "content": "---\ntitle: Advanced usage\nweight: 3\n---\n"
  },
  {
    "path": "docs/advanced-usage/cache.md",
    "content": "---\ntitle: Cache\nweight: 5\n---\n\nRole and Permission data are cached to speed up performance.\n\n## Automatic Cache Refresh Using Built-In Functions\n\nWhen you **use the built-in functions** for manipulating roles and permissions, the cache is automatically reset for you, and relations are automatically reloaded for the current model record:\n\n```php\n// When handling permissions assigned to roles:\n$role->givePermissionTo('edit articles');\n$role->revokePermissionTo('edit articles');\n$role->syncPermissions(params);\n\n// When linking roles to permissions:\n$permission->assignRole('writer');\n$permission->removeRole('writer');\n$permission->syncRoles(params);\n```\n\nHOWEVER, if you manipulate permission/role data directly in the database instead of calling the supplied methods, then you will not see the changes reflected in the application unless you manually reset the cache.\n\nAdditionally, because the Role and Permission models are Eloquent models which implement the `RefreshesPermissionCache` trait, creating and deleting Roles and Permissions will automatically clear the cache. If you have created your own models which do not extend the default models then you will need to implement the trait yourself.\n\n**NOTE: User-specific role/permission assignments are kept in-memory.**\n\nExamples:\n```php\n// These operations on a User do not call a cache-reset, because the User-related assignments are in-memory.\n$user->assignRole('writer');\n$user->removeRole('writer');\n$user->syncRoles(params);\n```\n\n## Manual cache reset\nTo manually reset the cache for this package, you can run the following in your app code:\n```php\napp()->make(\\Spatie\\Permission\\PermissionRegistrar::class)->forgetCachedPermissions();\n```\n\nOr you can use an Artisan command:\n```bash\nphp artisan permission:cache-reset\n```\n(This command is effectively an alias for `artisan cache:forget spatie.permission.cache` but respects the package config as well.)\n\n## Octane cache reset\nIn many cases Octane will not need additional cache resets; however, if you find that cache results are stale or crossing over between requests, you can force a cache flush upon every Octane reset cycle by editing the `/config/permission.php` and setting `register_octane_reset_listener` to true.\n\n## Cache Configuration Settings\n\nThis package allows you to customize cache-related operations via its config file. In most cases the defaults are fine; however, in a multitenancy situation you may wish to do some cache-prefix overrides when switching tenants. See below for more details.\n\n### Cache Expiration Time\n\nThe default cache `expiration_time` is `24 hours`.\nIf you wish to alter the expiration time you may do so in the `config/permission.php` file, in the `cache` array.\n\n\n### Cache Key\n\nThe default cache key is `spatie.permission.cache`.\nWe recommend not changing the cache \"key\" name. Usually changing it is a bad idea. More likely setting the cache `prefix` is better, as mentioned below.\n\n\n### Cache Identifier / Prefix\n\nLaravel Tip: If you are leveraging a caching service such as `redis` or `memcached` and there are other sites running on your server, you could run into cache clashes between apps. \n\nTo prevent other applications from accidentally using/changing your cached data, it is prudent to set your own cache `prefix` in Laravel's `/config/cache.php` to something unique for each application which shares the same caching service.\n\nMost multi-tenant \"packages\" take care of this for you when switching tenants. Optionally you might need to change cache boot order by writing a custom [cache boostrapper](https://github.com/spatie/laravel-permission/discussions/2310#discussioncomment-10855389).\n\nTip: Most parts of your multitenancy app will relate to a single tenant during a given request lifecycle, so the following step will not be needed: However, in the less-common situation where your app might be switching between multiple tenants during a single request lifecycle (specifically: where changing the cache key/prefix (such as when switching between tenants) or switching the cache store), then after switching tenants or changing the cache configuration you will need to reinitialize the cache of the `PermissionRegistrar` so that the updated `CacheStore` and cache configuration are used.\n\n```php\napp()->make(\\Spatie\\Permission\\PermissionRegistrar::class)->initializeCache();\n```\n\n\n### Custom Cache Store\n\nYou can configure the package to use any of the Cache Stores you've configured in Laravel's `config/cache.php`. This way you can point this package's caching to its own specified resource.\n\nIn `config/permission.php` set `cache.store` to the name of any one of the `config/cache.php` stores you've defined.\n\n## Disabling Cache\n\nSetting `'cache.store' => 'array'` in `config/permission.php` will effectively disable caching by this package between requests (it will only cache in-memory until the current request is completed processing, never persisting it).\n\nAlternatively, in development mode you can bypass ALL of Laravel's caching between visits by setting `CACHE_DRIVER=array` in `.env`. You can see an example of this in the default `phpunit.xml` file that comes with a new Laravel install. Of course, don't do this in production though!\n\n\n## File cache Store\n\nThis situation is not specific to this package, but is mentioned here due to the common question being asked.\n\nIf you are using the `File` cache Store and run into problems clearing the cache, it is most likely because your filesystem's permissions are preventing the PHP CLI from altering the cache files because the PHP-FPM process is running as a different user. \n\nWork with your server administrator to fix filesystem ownership on your cache files.\n\n## Database cache Store\n\nTIP: If you have `CACHE_STORE=database` set in your `.env`, remember that [you must install Laravel's cache tables via a migration before performing any cache operations](https://laravel.com/docs/cache#prerequisites-database). If you fail to install those migrations, you'll run into errors like `Call to a member function perform() on null` when the cache store attempts to purge or update the cache. This package does strategic cache resets in various places, so may trigger that error if your app's cache dependencies aren't set up.\n\n"
  },
  {
    "path": "docs/advanced-usage/custom-permission-check.md",
    "content": "---\ntitle: Custom Permission Check\nweight: 6\n---\n\n## Default Permission Check Functionality\nBy default, this package registers a `Gate::before()` method call on [Laravel's gate](https://laravel.com/docs/authorization). This method is responsible for checking if the user has the required permission or not, for calls to `can()` helpers and most `model policies`. Whether a user has a permission or not is determined by checking the user's permissions stored in the database.\n\nIn the permission config file, `register_permission_check_method` is set to `true`, which means this package operates using the default behavior described above. Only set this to `false` if you want to bypass the default operation and implement your own custom logic for checking permissions, as described below.\n\n## Using Custom Permission Check Functionality\n\nHowever, in some cases, you might want to implement custom logic for checking if the user has a permission or not.\n\nLet's say that your application uses access tokens for authentication and when issuing the tokens, you add a custom claim containing all the permissions the user has. In this case, if you want to check whether the user has the required permission or not based on the permissions in your custom claim in the access token, then you need to implement your own logic for handling this.\n\nYou could, for example, create a `Gate::before()` method call to handle this:\n\n**/app/Providers/AppServiceProvider.php**\n```php\nuse Illuminate\\Support\\Facades\\Gate;\n\npublic function boot(): void\n{\n    ...\n\n    Gate::before(function ($user, $ability) {\n        return $user->hasTokenPermission($ability) ?: null;\n    });\n}\n```\nHere `hasTokenPermission` is a **custom method you need to implement yourself, or call some other method on your model**.\n\n"
  },
  {
    "path": "docs/advanced-usage/events.md",
    "content": "---\ntitle: Events\nweight: 5\n---\n\nBy default Events are not enabled, because not all apps need to fire events related to roles and permissions.\n\nHowever, you may enable events by setting the `events_enabled => true` in `config/permission.php`\n\nNote that the events can receive the role or permission details as a model ID or as an Eloquent record, or as an array or collection of ids or records. Be sure to inspect the parameter before acting on it.\n\n\n## Available Events\n\nThe following events are available since `v7.0.0`:\n\n```\n\\Spatie\\Permission\\Events\\RoleAttachedEvent::class\n\\Spatie\\Permission\\Events\\RoleDetachedEvent::class\n\\Spatie\\Permission\\Events\\PermissionAttachedEvent::class\n\\Spatie\\Permission\\Events\\PermissionDetachedEvent::class\n```\n\n\n\nBetween `v6.15.0` and `v7.0.0` the events did not have \"Event\" as a suffix, so they were:\n\n```\n\\Spatie\\Permission\\Events\\RoleAttached::class\n\\Spatie\\Permission\\Events\\RoleDetached::class\n\\Spatie\\Permission\\Events\\PermissionAttached::class\n\\Spatie\\Permission\\Events\\PermissionDetached::class\n```\n"
  },
  {
    "path": "docs/advanced-usage/exceptions.md",
    "content": "---\ntitle: Exceptions\nweight: 3\n---\n\nIf you need to override exceptions thrown by this package, you can simply use normal [Laravel practices for handling exceptions](https://laravel.com/docs/errors#rendering-exceptions).\n\nAn example is shown below for your convenience, but nothing here is specific to this package other than the name of the exception.\n\nYou can find all the exceptions added by this package in the code here: [https://github.com/spatie/laravel-permission/tree/main/src/Exceptions](https://github.com/spatie/laravel-permission/tree/main/src/Exceptions)\n\n\n**/bootstrap/app.php**\n```php\n\n->withExceptions(function (Exceptions $exceptions) {\n    $exceptions->render(function (\\Spatie\\Permission\\Exceptions\\UnauthorizedException $e, $request) {\n        return response()->json([\n            'responseMessage' => 'You do not have the required authorization.',\n            'responseStatus'  => 403,\n        ]);\n    });\n}\n```\n"
  },
  {
    "path": "docs/advanced-usage/extending.md",
    "content": "---\ntitle: Extending\nweight: 4\n---\n\n## Adding fields to your models\nYou can add your own migrations to make changes to the role/permission tables, as you would for adding/changing fields in any other tables in your Laravel project.\n\nFollowing that, you can add any necessary logic for interacting with those fields into your custom/extended Models.\n\nHere is an example of adding a 'description' field to your Permissions and Roles tables:\n\n```sh\nphp artisan make:migration add_description_to_permissions_tables\n```\nAnd in the migration file:\n```php\npublic function up()\n{\n    Schema::table('permissions', function (Blueprint $table) {\n        $table->string('description')->nullable();\n    });\n    Schema::table('roles', function (Blueprint $table) {\n        $table->string('description')->nullable();\n    });\n}\n```\n\nSemi-Related article: [Adding Extra Fields To Pivot Table](https://quickadminpanel.com/blog/laravel-belongstomany-add-extra-fields-to-pivot-table/) (video)\n\n## Adding a description to roles and permissions\nA common question is \"how do I add a description for my roles or permissions?\".\n\nBy default, a 'description' field is not included in this package, to keep the model memory usage low, because not every app has a need for displayed descriptions.\n\nBut you are free to add it yourself if you wish. You can use the example above.\n\n### Multiple Language Descriptions\n\nIf you need your 'description' to support multiple languages, simply use Laravel's built-in language features. You might prefer to rename the 'description' field in these migration examples from 'description' to 'description_key' for clarity.\n\n\n## Extending User Models\nLaravel's authorization features are available in models which implement the `Illuminate\\Foundation\\Auth\\Access\\Authorizable` trait. \n\nBy default Laravel does this in `\\App\\Models\\User` by extending `Illuminate\\Foundation\\Auth\\User`, in which the trait and `Illuminate\\Contracts\\Auth\\Access\\Authorizable` contract are declared.\n\nIf you are creating your own User models and wish Authorization features to be available, you need to implement `Illuminate\\Contracts\\Auth\\Access\\Authorizable` in one of those ways as well.\n\n## Child User Models\n\nDue to the nature of polymorphism and Eloquent's hard-coded mapping of model names in the database, setting relationships for child models that inherit permissions of the parent can be difficult (even near impossible depending on app requirements, especially when attempting to do inverse mappings). However, one thing you might consider if you need the child model to never have its own permissions/roles but to only use its parent's permissions/roles, is to [override the `getMorphClass` method on the model](https://github.com/laravel/framework/issues/17830#issuecomment-345619085).\n\neg: This could be useful, but only if you're willing to give up the child's independence for roles/permissions:\n```php\n    public function getMorphClass()\n    {\n        return 'users';\n    }\n```\n\n## Extending Role and Permission Models\nIf you are extending or replacing the role/permission models, you will need to specify your new models in this package's `config/permission.php` file. \n\nFirst be sure that you've published the configuration file (see the Installation instructions), and edit it to update the `models.role` and `models.permission` values to point to your new models.\n\nNote the following requirements when extending/replacing the models: \n\n### Extending\nIf you need to EXTEND the existing `Role` or `Permission` models note that:\n\n- Your `Role` model needs to `extend` the `Spatie\\Permission\\Models\\Role` model\n- Your `Permission` model needs to `extend` the `Spatie\\Permission\\Models\\Permission` model\n- You need to update `config/permission.php` to specify your namespaced model\n\neg:\n```php\n<?php\nnamespace App\\Models;\nuse Spatie\\Permission\\Models\\Role as SpatieRole;\n\nclass Role extends SpatieRole\n{\n    // You might set a public property like guard_name or connection, or override other Eloquent Model methods/properties\n}\n```\n\n\n### Replacing\nIn MOST cases you will only EXTEND the models as described above.\nIn the rare case that you have need to REPLACE the existing `Role` or `Permission` models you need to keep the following things in mind:\n\n- If you are REPLACING and NOT EXTENDING the existing Model, do the following (and do NOT extend as described above):\n- Your `Role` model needs to implement the `Spatie\\Permission\\Contracts\\Role` contract\n- Your `Permission` model needs to implement the `Spatie\\Permission\\Contracts\\Permission` contract\n- You need to update `config/permission.php` to specify your namespaced model\n\n"
  },
  {
    "path": "docs/advanced-usage/other.md",
    "content": "---\ntitle: Other\nweight: 9\n---\n\n**Schema Diagram:**\n\nYou can find a schema diagram at [https://drawsql.app/templates/laravel-permission](https://drawsql.app/templates/laravel-permission)\n"
  },
  {
    "path": "docs/advanced-usage/phpstorm.md",
    "content": "---\ntitle: PhpStorm Interaction\nweight: 8\n---\n\n## Extending PhpStorm \n\n> **Note**\n> When using Laravel Idea plugin all directives are automatically added.\n\nYou may wish to extend PhpStorm to support Blade Directives of this package.\n\n1. In PhpStorm, open Preferences, and navigate to **Languages and Frameworks -> PHP -> Blade**\n(File | Settings | Languages & Frameworks | PHP | Blade)\n2. Uncheck \"Use default settings\", then click on the `Directives` tab.\n3. Add the following new directives for the laravel-permission package:\n\n\n**role**\n\n- has parameter = YES\n- Prefix: `<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {`\n- Suffix: `})): ?>`\n\n--\n\n**elserole**\n\n- has parameter = YES\n- Prefix: `<?php elseif(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {`\n- Suffix: `})): ?>`\n\n**endrole**\n\n- has parameter = NO\n- Prefix: blank\n- Suffix: blank\n\n--\n\n**hasrole**\n\n- has parameter = YES\n- Prefix: `<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {`\n- Suffix: `})): ?>`\n\n--\n\n**endhasrole**\n\n- has parameter = NO\n- Prefix: blank\n- Suffix: blank\n\n--\n\n**hasanyrole**\n\n- has parameter = YES\n- Prefix: `<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasAnyRole', {`\n- Suffix: `})): ?>`\n\n--\n\n**endhasanyrole**\n\n- has parameter = NO\n- Prefix: blank\n- Suffix: blank\n\n--\n\n**hasallroles**\n\n- has parameter = YES\n- Prefix: `<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasAllRoles', {`\n- Suffix: `})): ?>`\n\n--\n\n**endhasallroles**\n\n- has parameter = NO\n- Prefix: blank\n- Suffix: blank\n\n--\n\n**unlessrole**\n\n- has parameter = YES\n- Prefix: `<?php if(! \\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {`\n- Suffix: `})): ?>`\n\n--\n\n**endunlessrole**\n\n- has parameter = NO\n- Prefix: blank\n- Suffix: blank\n\n--\n\n**hasexactroles**\n\n- has parameter = YES\n- Prefix: `<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasExactRoles', {`\n- Suffix: `})): ?>`\n\n--\n\n**endhasexactroles**\n\n- has parameter = NO\n- Prefix: blank\n- Suffix: blank\n"
  },
  {
    "path": "docs/advanced-usage/seeding.md",
    "content": "---\ntitle: Database Seeding\nweight: 2\n---\n\n## Flush cache before/after seeding\n\nYou may discover that it is best to flush this package's cache **BEFORE seeding, to avoid cache conflict errors**.\n\nAnd if you use the `WithoutModelEvents` trait in your seeders, flush it **AFTER creating any roles/permissions as well, before assigning or granting them**.\n\n```php\n// reset cached roles and permissions\napp()[\\Spatie\\Permission\\PermissionRegistrar::class]->forgetCachedPermissions();\n```\n\nYou can optionally flush the cache before seeding by using the `SetUp()` method of your test suite (see the Testing page in the docs).\n\nOr it can be done directly in a seeder class, as shown below.\n\n## Database Cache Store\n\nTIP: If you have `CACHE_STORE=database` set in your `.env`, remember that [you must install Laravel's cache tables via a migration before performing any cache operations](https://laravel.com/docs/cache#prerequisites-database). If you fail to install those migrations, you'll run into errors like `Call to a member function perform() on null` when the cache store attempts to purge or update the cache. This package does strategic cache resets in various places, so may trigger that error if your app's cache dependencies aren't set up.\n\n## Roles/Permissions Seeder\n\nHere is a sample seeder, which first clears the cache, creates permissions and then assigns permissions to roles (the order of these steps is intentional):\n\n```php\nuse Illuminate\\Database\\Seeder;\nuse Spatie\\Permission\\Models\\Role;\nuse Spatie\\Permission\\Models\\Permission;\n\nclass RolesAndPermissionsSeeder extends Seeder\n{\n    public function run(): void\n    {\n        // Reset cached roles and permissions\n        app()[\\Spatie\\Permission\\PermissionRegistrar::class]->forgetCachedPermissions();\n\n        // create permissions\n        Permission::create(['name' => 'edit articles']);\n        Permission::create(['name' => 'delete articles']);\n        Permission::create(['name' => 'publish articles']);\n        Permission::create(['name' => 'unpublish articles']);\n\n        // update cache to know about the newly created permissions (required if using WithoutModelEvents in seeders)\n        app()[\\Spatie\\Permission\\PermissionRegistrar::class]->forgetCachedPermissions();\n\n\n        // create roles and assign created permissions\n\n        // this can be done as separate statements\n        $role = Role::create(['name' => 'writer']);\n        $role->givePermissionTo('edit articles');\n\n        // or may be done by chaining\n        $role = Role::create(['name' => 'moderator'])\n            ->givePermissionTo(['publish articles', 'unpublish articles']);\n\n        $role = Role::create(['name' => 'super-admin']);\n        $role->givePermissionTo(Permission::all());\n    }\n}\n```\n\n## User Seeding with Factories and States\n\nTo use Factory States to assign roles after creating users:\n\n```php\n// Factory:\n    public function definition() {...}\n\n    public function active(): static\n    {\n        return $this->state(fn (array $attributes) => [\n            'status' => 1,\n            ])\n            ->afterCreating(function (User $user) {\n                $user->assignRole('ActiveMember');\n            });\n    }\n\n// Seeder:\n// To create 4 users using this 'active' state in a Seeder:\nUser::factory(4)->active()->create();\n```\n\nTo seed multiple users and then assign each of them a role, WITHOUT using Factory States:\n\n```php\n// Seeder:\nUser::factory()\n    ->count(50)\n    ->create()\n    ->each(function ($user) {\n        $user->assignRole('Member');\n    });\n```\n\n\n## Speeding up seeding for large data sets\n\nWhen seeding large quantities of roles or permissions you may consider using Eloquent's `insert` command instead of `create`, as this bypasses all the internal checks that this package does when calling `create` (including extra queries to verify existence, test guards, etc).\n\n```php\n    $arrayOfPermissionNames = ['writer', 'editor'];\n    $permissions = collect($arrayOfPermissionNames)->map(function ($permission) {\n        return ['name' => $permission, 'guard_name' => 'web'];\n    });\n\n    Permission::insert($permissions->toArray());\n```\n\nAlternatively you could use `DB::insert`, as long as you also provide all the required data fields. One example of this is shown below ... but note that this example hard-codes the table names and field names, thus does not respect any customizations you may have in your permissions config file.\n\n```php\n$permissionsByRole = [\n    'admin' => ['restore posts', 'force delete posts'],\n    'editor' => ['create a post', 'update a post', 'delete a post'],\n    'viewer' => ['view all posts', 'view a post']\n];\n\n$insertPermissions = fn ($role) => collect($permissionsByRole[$role])\n    ->map(fn ($name) => DB::table('permissions')->insertGetId(['name' => $name, 'guard_name' => 'web']))\n    ->toArray();\n\n$permissionIdsByRole = [\n    'admin' => $insertPermissions('admin'),\n    'editor' => $insertPermissions('editor'),\n    'viewer' => $insertPermissions('viewer')\n];\n\nforeach ($permissionIdsByRole as $role => $permissionIds) {\n    $role = Role::whereName($role)->first();\n\n    DB::table('role_has_permissions')\n        ->insert(\n            collect($permissionIds)->map(fn ($id) => [\n                'role_id' => $role->id,\n                'permission_id' => $id\n            ])->toArray()\n        );\n}\n\n// and also add the command to flush the cache again now after doing all these inserts\n```\n\n**CAUTION**: ANY TIME YOU DIRECTLY RUN DB QUERIES you are bypassing cache-control features. So you will need to manually flush the package cache AFTER running direct DB queries, even in a seeder.\n"
  },
  {
    "path": "docs/advanced-usage/testing.md",
    "content": "---\ntitle: Testing\nweight: 1\n---\n\n## Clear Cache During Tests\n\nIn your application's tests, if you are not seeding roles and permissions as part of your test `setUp()` then you may run into a chicken/egg situation where roles and permissions aren't registered with the gate (because your tests create them after that gate registration is done). Working around this is simple: \n\nIn your tests simply add a `setUp()` instruction to re-register the permissions, like this:\n\n```php\n    protected function setUp(): void\n    {\n        // first include all the normal setUp operations\n        parent::setUp();\n\n        // now de-register all the roles and permissions by clearing the permission cache\n        $this->app->make(\\Spatie\\Permission\\PermissionRegistrar::class)->forgetCachedPermissions();\n    }\n```\n\n## Clear Cache When Using Seeders\n\nIf you are using Laravel's `LazilyRefreshDatabase` trait, you most likely want to avoid seeding permissions before every test, because that would negate the use of the `LazilyRefreshDatabase` trait. To overcome this, you should wrap your seeder in an event listener for the `DatabaseRefreshed` event:\n\n```php\nEvent::listen(DatabaseRefreshed::class, function () {\n    $this->artisan('db:seed', ['--class' => RoleAndPermissionSeeder::class]);\n    $this->app->make(\\Spatie\\Permission\\PermissionRegistrar::class)->forgetCachedPermissions();\n});\n```\n\nNote that `PermissionRegistrar::forgetCachedPermissions()` is called AFTER seeding. This is to prevent a caching issue that can occur when the database is set up after permissions have already been registered and cached. \n\n\n## Bypassing Cache When Testing\n\nThe caching infrastructure for this package is \"always on\", but when running your test suite you may wish to reduce its impact.\n\nTwo things you might wish to explore include:\n\n- Change the cache driver to `array`. **Very often you will have already done this in your `phpunit.xml` configuration.**\n\n- Shorten cache lifetime to 1 second, by setting the config (not necessary if cache driver is set to `array`) in your test suite TestCase:\n\n   `'permission.cache.expiration_time' = \\DateInterval::createFromDateString('1 seconds')`\n\n\n## Testing Using Factories\n\nMany applications do not require using factories to create fake roles/permissions for testing, because they use a Seeder to create specific roles and permissions that the application uses; thus tests are performed using the declared roles and permissions.\n\nHowever, if your application allows users to define their own roles and permissions you may wish to use Model Factories to generate roles and permissions as part of your test suite.\n\nWhen using Laravel's class-based Model Factory features you will need to `extend` this package's `Role` and/or `Permission` model into your app's namespace, add the `HasFactory` trait to it, and define a model factory for it. Then you can use that factory in your seeders like any other factory related to your application's models.\n"
  },
  {
    "path": "docs/advanced-usage/timestamps.md",
    "content": "---\ntitle: Timestamps\nweight: 10\n---\n\n## Excluding Timestamps from JSON\n\nIf you want to exclude timestamps from JSON output of role/permission pivots, you can extend the Role and Permission models into your own App namespace and mark the pivot as hidden:\n\n```php\n    protected $hidden = ['pivot'];\n ```\n\n## Adding Timestamps to Pivots\n\nIf you want to add timestamps to your pivot tables, you can do it with a few steps:\n - update the tables by calling `$table->timestamps();` in a migration\n - extend the `Permission` and `Role` models and add `->withTimestamps();` to the `BelongsToMany` relationshps for `roles()` and `permissions()`\n - update your `User` models (wherever you use the `HasRoles` or `HasPermissions` traits) by adding `->withTimestamps();` to the `BelongsToMany` relationships for `roles()` and `permissions()`\n"
  },
  {
    "path": "docs/advanced-usage/ui-options.md",
    "content": "---\ntitle: UI Options\nweight: 11\n---\n\n## Need a UI?\n\nThe package doesn't come with any UI/screens out of the box, you should build that yourself. \n\nBut: [do you really need a UI? Consider what Aaron and Joel have to say in this podcast episode](https://show.nocompromises.io/episodes/should-you-manage-roles-and-permissions-with-a-ui)\n\nIf you decide you need a UI, even if it's not for creating/editing role/permission names, but just for controlling which Users have access to which roles/permissions, following are some options to get you started:\n\n- [Code With Tony - video series](https://www.youtube.com/watch?v=lGfV1ddMhHA) to create an admin panel for managing roles and permissions in Laravel 9.\n\n- [FilamentPHP plugin](https://filamentphp.com/plugins/tharinda-rodrigo-spatie-roles-permissions) to manage roles and permissions using this package. (There are a few other Filament plugins which do similarly; use whichever suits your needs best.)\n\n- If you'd like to build your own UI, and understand the underlying logic for Gates and Roles and Users, the [Laravel 6  User Login and Management With Roles](https://www.youtube.com/watch?v=7PpJsho5aak&list=PLxFwlLOncxFLazmEPiB4N0iYc3Dwst6m4) video series by Mark Twigg of Penguin Digital gives thorough coverage to the topic, the theory, and implementation of a basic Roles system, independent of this Permissions Package.\n\n- [Laravel Nova package by @vyuldashev for managing Roles and Permissions](https://github.com/vyuldashev/nova-permission)\n\n- [Laravel Nova package by @paras-malhotra for managing Roles and Permissions and permissions based authorization for Nova Resources](https://github.com/insenseanalytics/laravel-nova-permission)\n\n- [How to create a UI for managing the permissions and roles](http://www.qcode.in/easy-roles-and-permissions-in-laravel-5-4/)\n\n- [Laravel User Management for managing users, roles, permissions, departments and authorization](https://github.com/Mekaeil/LaravelUserManagement) by [Mekaeil](https://github.com/Mekaeil)\n\n- [Generating UI boilerplate using InfyOm](https://youtu.be/hlGu2pa1bdU) video tutorial by [Shailesh](https://github.com/shailesh-ladumor)\n\n\n- [LiveWire Base Admin Panel](https://github.com/aliqasemzadeh/bap) User management by [AliQasemzadeh](https://github.com/aliqasemzadeh)\n\n- [JetAdmin](https://github.com/aliqasemzadeh/jetadmin) JetAdmin use laravel livewire starter kit and manage permissions. [AliQasemzadeh](https://github.com/aliqasemzadeh)\n\n- [QuickPanel](https://github.com/aliqasemzadeh/quickpanel) Quick Panel (TALL Flowbite Starter Kit). [AliQasemzadeh](https://github.com/aliqasemzadeh)\n"
  },
  {
    "path": "docs/advanced-usage/uuid.md",
    "content": "---\ntitle: UUID/ULID\nweight: 7\n---\n\nIf you're using UUIDs (ULID, GUID, etc) for your User models or Role/Permission models there are a few considerations to note.\n\n> NOTE: THIS IS NOT A FULL LESSON ON HOW TO IMPLEMENT UUIDs IN YOUR APP.\n\nSince each UUID implementation approach is different, some of these may or may not benefit you. As always, your implementation may vary.\n\nWe use \"uuid\" in the examples below. Adapt for ULID or GUID as needed.\n\n## Migrations\nYou will need to update the `create_permission_tables.php` migration after creating it with `php artisan vendor:publish`. After making your edits, be sure to run the migration!\n\n**User Models using UUIDs**\nIf your User models are using `uuid` instead of `unsignedBigInteger` then you'll need to reflect the change in the migration provided by this package. Something like the following would be typical, for **both** `model_has_permissions` and `model_has_roles` tables:\n\n```diff\n// note: this is done in two places in the default migration file, so edit both places:\n-  $table->unsignedBigInteger($columnNames['model_morph_key'])\n+  $table->uuid($columnNames['model_morph_key'])\n```\n\n**Roles and Permissions using UUIDS**\nIf you also want the roles and permissions to use a UUID for their `id` value, then you'll need to change the id fields accordingly, and manually set the primary key.\n\n```diff\n    Schema::create($tableNames['permissions'], function (Blueprint $table) {\n-        $table->bigIncrements('id'); // permission id\n+        $table->uuid('uuid')->primary()->unique(); // permission id\n//...\n    });\n\n    Schema::create($tableNames['roles'], function (Blueprint $table) {\n-        $table->bigIncrements('id'); // role id\n+        $table->uuid('uuid')->primary()->unique(); // role id\n//...\n    });\n\n    Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames) {\n-        $table->unsignedBigInteger($pivotPermission);\n+        $table->uuid($pivotPermission);\n        $table->string('model_type');\n//...\n        $table->foreign($pivotPermission)\n-            ->references('id') // permission id\n+            ->references('uuid') // permission id\n            ->on($tableNames['permissions'])\n            ->onDelete('cascade');\n//...\n\n    Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames) {\n-        $table->unsignedBigInteger($pivotRole);\n+        $table->uuid($pivotRole);\n//...\n        $table->foreign($pivotRole)\n-            ->references('id') // role id\n+            ->references('uuid') // role id\n            ->on($tableNames['roles'])\n            ->onDelete('cascade');//...\n\n    Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {\n-        $table->unsignedBigInteger($pivotPermission);\n-        $table->unsignedBigInteger($pivotRole);\n+        $table->uuid($pivotPermission);\n+        $table->uuid($pivotRole);\n\n         $table->foreign($pivotPermission)\n-            ->references('id') // permission id\n+            ->references('uuid') // permission id\n            ->on($tableNames['permissions'])\n            ->onDelete('cascade');\n\n         $table->foreign($pivotRole)\n-            ->references('id') // role id\n+            ->references('uuid') // role id\n            ->on($tableNames['roles'])\n            ->onDelete('cascade'); \n```\n\n\n## Configuration (OPTIONAL)\nYou might want to change the pivot table field name from `model_id` to `model_uuid`, just for semantic purposes.\nFor this, in the `permission.php` configuration file edit `column_names.model_morph_key`:\n\n- OPTIONAL: Change to `model_uuid` instead of the default `model_id`.\n```diff\n        'column_names' => [    \n        /*\n         * Change this if you want to name the related pivots other than defaults\n         */\n        'role_pivot_key' => null, //default 'role_id',\n        'permission_pivot_key' => null, //default 'permission_id',\n\n        /*\n         * Change this if you want to name the related model primary key other than\n         * `model_id`.\n         *\n         * For example, this would be nice if your primary keys are all UUIDs. In\n         * that case, name this `model_uuid`.\n         */\n-            'model_morph_key' => 'model_id',\n+            'model_morph_key' => 'model_uuid',\n        ],\n```\n- If you extend the models into your app, be sure to list those models in your `permissions.php` configuration file. See the Extending section of the documentation and the Models section below.\n\n## Models\nIf you want all the role/permission objects to have a UUID instead of an integer, you will need to Extend the default Role and Permission models into your own namespace in order to set some specific properties. (See the Extending section of the docs, where it explains requirements of Extending, as well as the `permissions.php` configuration settings you need to update.)\n\nExamples:\n\nCreate new models, which extend the Role and Permission models of this package, and add Laravel's `HasUuids` trait (available since Laravel 9):\n```bash\nphp artisan make:model Role\nphp artisan make:model Permission\n```\n\n`App\\Model\\Role.php`\n```php\n<?php\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Concerns\\HasUuids;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Spatie\\Permission\\Models\\Role as SpatieRole;\n\nclass Role extends SpatieRole\n{\n    use HasFactory;\n    use HasUuids;\n    protected $primaryKey = 'uuid';\n}\n```\n\n`App\\Model\\Permission.php`\n```php\n<?php\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Concerns\\HasUuids;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Spatie\\Permission\\Models\\Permission as SpatiePermission;\n\nclass Permission extends SpatiePermission\n{\n    use HasFactory;\n    use HasUuids;\n    protected $primaryKey = 'uuid';\n}\n```\nAnd edit `config/permission.php`\n```diff\n    'models' => [\n\n        /*\n         * When using the \"HasPermissions\" trait from this package, we need to know which\n         * Eloquent model should be used to retrieve your permissions. Of course, it\n         * is often just the \"Permission\" model but you may use whatever you like.\n         *\n         * The model you want to use as a Permission model needs to implement the\n         * `Spatie\\Permission\\Contracts\\Permission` contract.\n         */\n\n-        'permission' => Spatie\\Permission\\Models\\Permission::class\n+        'permission' => \\App\\Models\\Permission::class,\n\n        /*\n         * When using the \"HasRoles\" trait from this package, we need to know which\n         * Eloquent model should be used to retrieve your roles. Of course, it\n         * is often just the \"Role\" model but you may use whatever you like.\n         *\n         * The model you want to use as a Role model needs to implement the\n         * `Spatie\\Permission\\Contracts\\Role` contract.\n         */\n\n-        'role' => Spatie\\Permission\\Models\\Role::class,\n+        'role' => \\App\\Models\\Role::class,\n\n    ],\n```\n"
  },
  {
    "path": "docs/basic-usage/_index.md",
    "content": "---\ntitle: Basic Usage\nweight: 1\n---\n"
  },
  {
    "path": "docs/basic-usage/artisan.md",
    "content": "---\ntitle: Artisan Commands\nweight: 10\n---\n\n## Creating roles and permissions with Artisan Commands\n\nYou can create a role or permission from the console with artisan commands.\n\n```bash\nphp artisan permission:create-role writer\n```\n\n```bash\nphp artisan permission:create-permission \"edit articles\"\n```\n\nWhen creating permissions/roles for specific guards you can specify the guard names as a second argument:\n\n```bash\nphp artisan permission:create-role writer web\n```\n\n```bash\nphp artisan permission:create-permission \"edit articles\" web\n```\n\nWhen creating roles you can also create and link permissions at the same time:\n\n```bash\nphp artisan permission:create-role writer web \"create articles|edit articles\"\n```\n\nWhen creating roles with teams enabled you can set the team id by adding the `--team-id` parameter:\n\n```bash\nphp artisan permission:create-role --team-id=1 writer\nphp artisan permission:create-role writer api --team-id=1\n```\n\n## Displaying roles and permissions in the console\n\nThere is also a `show` command to show a table of roles and permissions per guard:\n\n```bash\nphp artisan permission:show\n```\n\n## Resetting the Cache\n\nWhen you use the built-in functions for manipulating roles and permissions, the cache is automatically reset for you, and relations are automatically reloaded for the current model record.\n\nSee the Advanced-Usage/Cache section of these docs for detailed specifics.\n\nIf you need to manually reset the cache for this package, you may use the following artisan command:\n\n```bash\nphp artisan permission:cache-reset\n```\n\nAgain, it is more efficient to use the API provided by this package, instead of manually clearing the cache.\n"
  },
  {
    "path": "docs/basic-usage/basic-usage.md",
    "content": "---\ntitle: Basic Usage\nweight: 1\n---\n\n## Add The Trait\nFirst, add the `Spatie\\Permission\\Traits\\HasRoles` trait to your `User` model(s):\n\n```php\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Spatie\\Permission\\Traits\\HasRoles;\n\nclass User extends Authenticatable\n{\n    use HasRoles;\n\n    // ...\n}\n```\n\n## Create A Permission\nThis package allows for users to be associated with permissions and roles. Every role is associated with multiple permissions.\nA `Role` and a `Permission` are regular Eloquent models. They require a `name` and can be created like this:\n\n```php\nuse Spatie\\Permission\\Models\\Role;\nuse Spatie\\Permission\\Models\\Permission;\n\n$role = Role::create(['name' => 'writer']);\n$permission = Permission::create(['name' => 'edit articles']);\n```\n\n## Assign A Permission To A Role\nA permission can be assigned to a role using either of these methods:\n\n```php\n$role->givePermissionTo($permission);\n$permission->assignRole($role);\n```\n\n## Sync Permissions To A Role\nMultiple permissions can be synced to a role using either of these methods:\n\n```php\n$role->syncPermissions($permissions);\n$permission->syncRoles($roles);\n```\n\n## Remove Permission From A Role\nA permission can be removed from a role using either of these methods:\n\n```php\n$role->revokePermissionTo($permission);\n$permission->removeRole($role);\n```\n\n## Guard Name\nIf you're using multiple guards then the `guard_name` attribute must be set as well. Read about it in the [using multiple guards](./multiple-guards) documentation.\n\n## Get Permissions For A User\nThe `HasRoles` trait adds Eloquent relationships to your models, which can be accessed directly or used as a base query:\n\n```php\n// get a list of all permissions directly assigned to the user\n$permissionNames = $user->getPermissionNames(); // collection of name strings\n$permissions = $user->permissions; // collection of permission objects\n\n// get all permissions for the user, either directly, or from roles, or from both\n$permissions = $user->getDirectPermissions();\n$permissions = $user->getPermissionsViaRoles();\n$permissions = $user->getAllPermissions();\n\n// get the names of the user's roles\n$roles = $user->getRoleNames(); // Returns a collection\n```\n\n## Scopes\nThe `HasRoles` trait also adds `role` and `withoutRole` scopes to your models to scope the query to certain roles or permissions:\n\n```php\n$users = User::role('writer')->get(); // Returns only users with the role 'writer'\n$nonEditors = User::withoutRole('editor')->get(); // Returns only users without the role 'editor'\n```\n\nThe `role` and `withoutRole` scopes can accept a string, a `\\Spatie\\Permission\\Models\\Role` object or an `\\Illuminate\\Support\\Collection` object.\n\nThe same trait also adds scopes to only get users that have or don't have a certain permission.\n\n```php\n$users = User::permission('edit articles')->get(); // Returns only users with the permission 'edit articles' (inherited or directly)\n$usersWhoCannotEditArticles = User::withoutPermission('edit articles')->get(); // Returns all users without the permission 'edit articles' (inherited or directly)\n```\n\nThe scope can accept a string, a `\\Spatie\\Permission\\Models\\Permission` object or an `\\Illuminate\\Support\\Collection` object.\n\n\n## Eloquent Calls\nSince Role and Permission models are extended from Eloquent models, basic Eloquent calls can be used as well:\n\n```php\n$allUsersWithAllTheirRoles = User::with('roles')->get();\n$allUsersWithAllTheirDirectPermissions = User::with('permissions')->get();\n$allRolesInDatabase = Role::all()->pluck('name');\n$usersWithoutAnyRoles = User::doesntHave('roles')->get();\n$allRolesExceptAandB = Role::whereNotIn('name', ['role A', 'role B'])->get();\n```\n\n## Counting Users Having A Role\nOne way to count all users who have a certain role is by filtering the collection of all Users with their Roles:\n```php\n$managersCount = User::with('roles')->get()->filter(\n    fn ($user) => $user->roles->where('name', 'Manager')->toArray()\n)->count();\n```\n"
  },
  {
    "path": "docs/basic-usage/blade-directives.md",
    "content": "---\ntitle: Blade directives\nweight: 7\n---\n\n## Permissions\nThis package lets you use Laravel's native `@can` directive to check if a user has a certain permission (whether you gave them that permission directly or if you granted it indirectly via a role):\n\n```php\n@can('edit articles')\n  //\n@endcan\n```\nor\n```php\n@if(auth()->user()->can('edit articles') && $some_other_condition)\n  //\n@endif\n```\n\nYou can use `@can`, `@cannot`, `@canany`, and `@guest` to test for permission-related access.\n\nWhen using a permission-name associated with permissions created in this package, you can use `@can('permission-name', 'guard_name')` if you need to check against a specific guard.\n\nExample:\n```php\n@can('edit articles', 'guard_name')\n  //\n@endcan\n```\n\nYou can also use `@haspermission('permission-name')` or `@haspermission('permission-name', 'guard_name')` in similar fashion. With corresponding `@endhaspermission`.\n\nThere is no `@hasanypermission` directive: use `@canany` instead.\n\n\n## Roles \nAs discussed in the Best Practices section of the docs, **it is strongly recommended to always use permission directives**, instead of role directives.\n\nAdditionally, if your reason for testing against Roles is for a Super-Admin, see the *Defining A Super-Admin* section of the docs.\n\nIf you actually need to directly test for Roles, this package offers some Blade directives to verify whether the currently logged in user has all or any of a given list of roles.\n\nOptionally you can pass in the `guard` that the check will be performed on as a second argument.\n\n## Blade and Roles\nCheck for a specific role:\n```php\n@role('writer')\n    I am a writer!\n@else\n    I am not a writer...\n@endrole\n```\nis the same as\n```php\n@hasrole('writer')\n    I am a writer!\n@else\n    I am not a writer...\n@endhasrole\n```\nwhich is also the same as\n```php\n@if(auth()->user()->hasRole('writer'))\n  //\n@endif\n```\n\nCheck for any role in a list:\n```php\n@hasanyrole($collectionOfRoles)\n    I have one or more of these roles!\n@else\n    I have none of these roles...\n@endhasanyrole\n// or\n@hasanyrole('writer|admin')\n    I am either a writer or an admin or both!\n@else\n    I have none of these roles...\n@endhasanyrole\n```\nCheck for all roles:\n\n```php\n@hasallroles($collectionOfRoles)\n    I have all of these roles!\n@else\n    I do not have all of these roles...\n@endhasallroles\n// or\n@hasallroles('writer|admin')\n    I am both a writer and an admin!\n@else\n    I do not have all of these roles...\n@endhasallroles\n```\n\nAlternatively, `@unlessrole` gives the reverse for checking a singular role, like this:\n\n```php\n@unlessrole('does not have this role')\n    I do not have the role\n@else\n    I do have the role\n@endunlessrole\n```\n\nYou can also determine if a user has exactly all of a given list of roles:\n\n```php\n@hasexactroles('writer|admin')\n    I am both a writer and an admin and nothing else!\n@else\n    I do not have all of these roles or have more other roles...\n@endhasexactroles\n```\n"
  },
  {
    "path": "docs/basic-usage/direct-permissions.md",
    "content": "---\ntitle: Direct Permissions\nweight: 2\n---\n\n## Best Practice\n\nINSTEAD OF DIRECT PERMISSIONS, it is better to assign permissions to Roles, and then assign Roles to Users.\n\nSee the [Roles vs Permissions](../best-practices/roles-vs-permissions) section of the docs for a deeper explanation.\n\nHOWEVER, If you have reason to directly assign individual permissions to specific users (instead of to roles which are assigned to those users), you can do that as well:\n\n## Direct Permissions to Users\n\n### Giving/Revoking direct permissions\n\nA permission can be given to any user:\n\n```php\n$user->givePermissionTo('edit articles');\n\n// You can also give multiple permission at once\n$user->givePermissionTo('edit articles', 'delete articles');\n\n// You may also pass an array\n$user->givePermissionTo(['edit articles', 'delete articles']);\n```\n\nA permission can be revoked from a user:\n\n```php\n$user->revokePermissionTo('edit articles');\n```\n\nOr revoke & add new permissions in one go:\n\n```php\n$user->syncPermissions(['edit articles', 'delete articles']);\n```\n\n## Checking Direct Permissions\nLike all permissions assigned via roles, you can check if a user has a permission by using Laravel's default `can` function. This will also allow you to use Super-Admin features provided by Laravel's Gate:\n\n```php\n$user->can('edit articles');\n```\n\n> NOTE: The following `hasPermissionTo`, `hasAnyPermission`, `hasAllPermissions` functions do not support Super-Admin functionality. Use `can`, `canAny` instead.\n\nYou can check if a user has a permission:\n\n```php\n$user->hasPermissionTo('edit articles');\n```\n\nOr you may pass an integer representing the permission id\n\n```php\n$user->hasPermissionTo('1');\n$user->hasPermissionTo(Permission::find(1)->id);\n$user->hasPermissionTo($somePermission->id);\n```\n\nYou can check if a user has Any of an array of permissions:\n\n```php\n$user->hasAnyPermission(['edit articles', 'publish articles', 'unpublish articles']);\n```\n\n...or if a user has All of an array of permissions:\n\n```php\n$user->hasAllPermissions(['edit articles', 'publish articles', 'unpublish articles']);\n```\n\nYou may also pass integers to lookup by permission id\n\n```php\n$user->hasAnyPermission(['edit articles', 1, 5]);\n```\n"
  },
  {
    "path": "docs/basic-usage/enums.md",
    "content": "---\ntitle: Enums\nweight: 4\n---\n\n## Enum Prerequisites\n\nInternally, Enums implicitly implement `\\BackedEnum`, which is how this package recognizes that you're passing an Enum.\n\nNOTE: Presently (versions 6, 7) this package does not support using `$casts` to specify enums on the `Permission` model. You can still use enums to reference things as shown below, just without declaring it in a `$casts` property.\n\n\n## Code Requirements\n\nYou can create your Enum object for use with Roles and/or Permissions. You will probably create separate Enums for Roles and for Permissions, although if your application needs are simple you might choose a single Enum for both.\n\nUsually the list of application Roles is much shorter than the list of Permissions, so having separate objects for them can make them easier to manage.\n\nHere is an example Enum for Roles. You would do similarly for Permissions.\n\n```php\nnamespace App\\Enums;\n\nenum RolesEnum: string\n{\n    // case NAMEINAPP = 'name-in-database';\n\n    case WRITER = 'writer';\n    case EDITOR = 'editor';\n    case USERMANAGER = 'user-manager';\n\n    // extra helper to allow for greater customization of displayed values, without disclosing the name/value data directly\n    public function label(): string\n    {\n        return match ($this) {\n            static::WRITER => 'Writers',\n            static::EDITOR => 'Editors',\n            static::USERMANAGER => 'User Managers',\n        };\n    }\n}\n```\n\n## Creating Roles/Permissions using Enums\n\nWhen **creating** roles/permissions, you cannot pass an Enum name directly, because Eloquent expects a string for the name.\n\nUse Laravel's `enum_value()` function for simplicity: \n\n\n```php\n  $role = app(Role::class)->findOrCreate(enum_value(RolesEnum::WRITER), 'web');\n```\nSame with creating Permissions.\n\n### Authorizing using Enums\n\nIn your application code, when checking for authorization using features of this package, you can use `MyEnum::NAME` directly in most cases, without passing `->value` to convert to a string.\n\nThere may occasionally be times where you will need to manually fallback to adding `->value` (eg: `MyEnum::NAME->value`) when using features that aren't aware of Enum support, such as when you need to pass `string` values instead of an `Enum` to a function that doesn't recognize Enums. Again, wrap it with `enum_value()` as a simple workaround.\n\nExamples:\n```php\n// the following are identical because `hasPermissionTo` is aware of `BackedEnum` support:\n$user->hasPermissionTo(PermissionsEnum::VIEWPOSTS);\n$user->hasPermissionTo(PermissionsEnum::VIEWPOSTS->value);\n$user->hasPermissionTo(enum_value(PermissionsEnum::VIEWPOSTS));\n\n// Blade directives:\n@can(enum_value(PermissionsEnum::VIEWPOSTS))\n```\n\n\n## Package methods supporting BackedEnums:\nThe following methods of this package support passing `BackedEnum` parameters directly:\n\n```php\n\t$user->assignRole(RolesEnum::WRITER);\n\t$user->removeRole(RolesEnum::EDITOR);\n\n    $role->givePermissionTo(PermissionsEnum::EDITPOSTS);\n    $role->revokePermissionTo(PermissionsEnum::EDITPOSTS);\n\n    $user->givePermissionTo(PermissionsEnum::EDITPOSTS);\n    $user->revokePermissionTo(PermissionsEnum::EDITPOSTS);\n\n\t$user->hasPermissionTo(PermissionsEnum::EDITPOSTS);\n\t$user->hasAnyPermission([PermissionsEnum::EDITPOSTS, PermissionsEnum::VIEWPOSTS]);\n\t$user->hasDirectPermission(PermissionsEnum::EDITPOSTS);\n    \n    $user->hasRole(RolesEnum::WRITER);\n    $user->hasAllRoles([RolesEnum::WRITER, RolesEnum::EDITOR]);\n    $user->hasExactRoles([RolesEnum::WRITER, RolesEnum::EDITOR, RolesEnum::MANAGER]);\n\n```\n"
  },
  {
    "path": "docs/basic-usage/middleware.md",
    "content": "---\ntitle: Middleware\nweight: 11\n---\n\n## Default Middleware\n\nFor checking against a single permission (see Best Practices) using `can`, you can use the built-in Laravel middleware provided by `\\Illuminate\\Auth\\Middleware\\Authorize::class` like this:\n\n```php\nRoute::group(['middleware' => ['can:publish articles']], function () { ... });\n\n// or with static method\nRoute::group(['middleware' => [\\Illuminate\\Auth\\Middleware\\Authorize::using('publish articles')]], function () { ... });\n```\n\n## Package Middleware\n\n**See a typo? Note that since v6 the _'Middleware'_ namespace is singular. Prior to v6 it was _'Middlewares'_. Time to upgrade your implementation!**\n\nThis package comes with `RoleMiddleware`, `PermissionMiddleware` and `RoleOrPermissionMiddleware` middleware.\n\nYou can register their aliases for easy reference elsewhere in your app:\n\nOpen `/bootstrap/app.php` and register them there:\n\n```php\n    ->withMiddleware(function (Middleware $middleware) {\n        $middleware->alias([\n            'role' => \\Spatie\\Permission\\Middleware\\RoleMiddleware::class,\n            'permission' => \\Spatie\\Permission\\Middleware\\PermissionMiddleware::class,\n            'role_or_permission' => \\Spatie\\Permission\\Middleware\\RoleOrPermissionMiddleware::class,\n        ]);\n    })\n```\n\n### Middleware Priority\nIf your app is triggering *404 Not Found* responses when a *403 Not Authorized* response might be expected, it might be a middleware priority clash. Explore reordering priorities so that this package's middleware runs before Laravel's `SubstituteBindings` middleware. (See [Middleware docs](https://laravel.com/docs/master/middleware#sorting-middleware) ). \n\nIf needed, you could optionally explore `$middleware->prependToGroup()` instead. See the Laravel Documentation for details.\n\n\n## Using Middleware in Routes and Controllers\n\nAfter you have registered the aliases as shown above, you can use them in your Routes and Controllers much the same way you use any other middleware: \n\n### Routes\n\n```php\nRoute::group(['middleware' => ['role:manager']], function () { ... });\nRoute::group(['middleware' => ['permission:publish articles']], function () { ... });\nRoute::group(['middleware' => ['role_or_permission:publish articles']], function () { ... });\n\n// for a specific guard:\nRoute::group(['middleware' => ['role:manager,api']], function () { ... });\n\n// multiple middleware\nRoute::group(['middleware' => ['role:manager','permission:publish articles']], function () { ... });\n```\n\nYou can specify multiple roles or permissions with a `|` (pipe) character, which is treated as `OR`:\n\n```php\nRoute::group(['middleware' => ['role:manager|writer']], function () { ... });\nRoute::group(['middleware' => ['permission:publish articles|edit articles']], function () { ... });\nRoute::group(['middleware' => ['role_or_permission:manager|edit articles']], function () { ... });\n\n// for a specific guard\nRoute::group(['middleware' => ['permission:publish articles|edit articles,api']], function () { ... });\n```\n\n### Controllers\n\nIf your controller implements the `HasMiddleware` interface, you can register [controller middleware](https://laravel.com/docs/12.x/controllers#controller-middleware) using the `middleware()` method:\n\n```php\npublic static function middleware(): array\n{\n    return [\n        // examples with aliases, pipe-separated names, guards, etc:\n        'role_or_permission:manager|edit articles',\n        new Middleware('role:author', only: ['index']),\n        new Middleware(\\Spatie\\Permission\\Middleware\\RoleMiddleware::using('manager'), except:['show']),\n        new Middleware(\\Spatie\\Permission\\Middleware\\PermissionMiddleware::using('delete records,api'), only:['destroy']),\n    ];\n}\n```\n\nYou can also use Laravel's Model Policy feature in your controller methods. See the Model Policies section of these docs.\n\n## Middleware via Static Methods\n\nAll of the middleware can also be applied by calling the static `using` method, which accepts either an array or a `|`-separated string as input.\n\n```php\nRoute::group(['middleware' => [\\Spatie\\Permission\\Middleware\\RoleMiddleware::using('manager')]], function () { ... });\nRoute::group(['middleware' => [\\Spatie\\Permission\\Middleware\\PermissionMiddleware::using('publish articles|edit articles')]], function () { ... });\nRoute::group(['middleware' => [\\Spatie\\Permission\\Middleware\\RoleOrPermissionMiddleware::using(['manager', 'edit articles'])]], function () { ... });\n```\n\n"
  },
  {
    "path": "docs/basic-usage/multiple-guards.md",
    "content": "---\ntitle: Using multiple guards\nweight: 9\n---\n\nWhen using the default Laravel auth configuration all of the core methods of this package will work out of the box, with no extra configuration required.\n\nHowever, when using multiple guards they will act like namespaces for your permissions and roles: Every guard has its own set of permissions and roles that can be assigned to its user model.\n\n## The Downside To Multiple Guards\n\nNote that this package requires you to register a permission name (same for roles) for each guard you want to authenticate with. So, \"edit-article\" would have to be created multiple times for each guard your app uses. An exception will be thrown if you try to authenticate against a non-existing permission+guard combination. Same for roles.\n\n> **Tip**: If your app uses only a single guard, but it is not `web` (Laravel's default, which shows \"first\" in the auth config file) then change the order of your listed guards in your `config/auth.php` to list your primary guard as the default and as the first in the list of defined guards. While you're editing that file, it is best to remove any guards you don't use, too.\n> \n> OR you could use the suggestion below to force the use of a single guard:\n\n## Forcing Use Of A Single Guard\n\nIf your app structure does NOT differentiate between guards when it comes to roles/permissions, (ie: if ALL your roles/permissions are the SAME for ALL guards), you can override the `getDefaultGuardName` function by adding it to your User model, and specifying your desired `$guard_name`. Then you only need to create roles/permissions for that single `$guard_name`, not duplicating them. The example here sets it to `web`, but use whatever your application's default is:\n\n```php\n    protected string $guard_name = 'web';\n    protected function getDefaultGuardName(): string { return $this->guard_name; }\n````\n\n\n## Using permissions and roles with multiple guards\n\nWhen creating new permissions and roles, if no guard is specified, then the **first** defined guard in `auth.guards` config array will be used. \n\n```php\n// Create a manager role for users authenticating with the admin guard:\n$role = Role::create(['guard_name' => 'admin', 'name' => 'manager']);\n\n// Define a `publish articles` permission for the admin users belonging to the admin guard\n$permission = Permission::create(['guard_name' => 'admin', 'name' => 'publish articles']);\n\n// Define a *different* `publish articles` permission for the regular users belonging to the web guard\n$permission = Permission::create(['guard_name' => 'web', 'name' => 'publish articles']);\n```\n\nTo check if a user has permission for a specific guard:\n\n```php\n$user->hasPermissionTo('publish articles', 'admin');\n```\n\n> **Note**: When determining whether a role/permission is valid on a given model, it checks against the first matching guard in this order (it does NOT check role/permission for EACH possibility, just the first match):\n- first the guardName() method if it exists on the model (may return a string or array);\n- then the `$guard_name` property if it exists on the model (may return a string or array);\n- then the first-defined guard/provider combination in the `auth.guards` config array that matches the loaded model's guard;\n- then the `auth.defaults.guard` config (which is the user's guard if they are logged in, else the default in the file).\n\n\n## Assigning permissions and roles to guard users\n\nYou can use the same core methods to assign permissions and roles to users; just make sure the `guard_name` on the permission or role matches the guard of the user, otherwise a `GuardDoesNotMatch` or `Role/PermissionDoesNotExist` exception will be thrown. \n\nIf your User is able to consume multiple roles or permissions from different guards; make sure the User class's `$guard_name` property or `guardName()` method returns all allowed guards as an array:\n\n```php\n    protected $guard_name = ['web', 'admin'];\n````\nor\n```php\n    public function guardName() { return ['web', 'admin']; }\n````\n\n## Using blade directives with multiple guards\n\nYou can use all of the blade directives offered by this package by passing in the guard you wish to use as the second argument to the directive:\n\n```php\n@role('super-admin', 'admin')\n    I am a super-admin!\n@else\n    I am not a super-admin...\n@endrole\n```\n"
  },
  {
    "path": "docs/basic-usage/new-app.md",
    "content": "---\ntitle: Example App\nweight: 90\n---\n\n## Creating A Demo App\n\nIf you want to just try out the features of this package you can get started with the following.\n\nThe examples on this page are primarily added for assistance in creating a quick demo app for troubleshooting purposes, to post the repo on github for convenient sharing to collaborate or get support.\n\nIf you're new to Laravel or to any of the concepts mentioned here, you can learn more in the [Laravel documentation](https://laravel.com/docs/) and in the free videos at Laracasts such as with the [Laravel 11 in 30 days](https://laracasts.com/series/30-days-to-learn-laravel-11) or [Laravel 8 From Scratch](https://laracasts.com/series/laravel-8-from-scratch/) series.\n\n### Initial setup:\n\n```sh\ncd ~/Sites\nlaravel new mypermissionsdemo\n# (No Starter Kit is needed, but you could go with Livewire or Breeze/Jetstream, with Laravel's Built-In-Auth; or use Bootstrap using laravel/ui described later, below)\n# (You might be asked to select a dark-mode-support choice)\n# (Choose your desired testing framework: Pest or PHPUnit)\n# (If offered, say Yes to initialize a Git repo, so that you can track your code changes)\n# (If offered a database selection, choose SQLite, because it is simplest for test scenarios)\n# (If prompted, say Yes to run default database migrations)\n# (If prompted, say Yes to run npm install and related commands)\n\ncd mypermissionsdemo\n\n# The following git commands are not needed if you Initialized a git repo while \"laravel new\" was running above:\ngit init\ngit add .\ngit commit -m \"Fresh Laravel Install\"\n\n# These Environment steps are not needed if you already selected SQLite while \"laravel new\" was running above:\ncp -n .env.example .env\nsed -i '' 's/DB_CONNECTION=mysql/DB_CONNECTION=sqlite/' .env\nsed -i '' 's/DB_DATABASE=/#DB_DATABASE=/' .env\ntouch database/database.sqlite\n\n# Package\ncomposer require spatie/laravel-permission\nphp artisan vendor:publish --provider=\"Spatie\\Permission\\PermissionServiceProvider\"\ngit add .\ngit commit -m \"Add Spatie Laravel Permissions package\"\nphp artisan migrate:fresh\n\n# Add `HasRoles` trait to User model\nsed -i '' $'s/use HasFactory, Notifiable;/use HasFactory, Notifiable;\\\\\\n    use \\\\\\\\Spatie\\\\\\\\Permission\\\\\\\\Traits\\\\\\\\HasRoles;/' app/Models/User.php\nsed -i '' $'s/use HasApiTokens, HasFactory, Notifiable;/use HasApiTokens, HasFactory, Notifiable;\\\\\\n    use \\\\\\\\Spatie\\\\\\\\Permission\\\\\\\\Traits\\\\\\\\HasRoles;/' app/Models/User.php\ngit add . && git commit -m \"Add HasRoles trait\"\n```\n\nIf you didn't install a Starter Kit like Livewire or Breeze or Jetstream, add Laravel's basic auth scaffolding:\nThis Auth scaffolding will make it simpler to provide login capability for a test/demo user, and test roles/permissions with them.\n```php\ncomposer require laravel/ui --dev\nphp artisan ui bootstrap --auth\n# npm install && npm run build\ngit add . && git commit -m \"Setup auth scaffold\"\n```\n\n### Add some basic permissions\n- Add a new file, `/database/seeders/PermissionsDemoSeeder.php` such as the following (You could create it with `php artisan make:seed` and then edit the file accordingly):\n\n```php\n<?php\n\nnamespace Database\\Seeders;\n\nuse Illuminate\\Database\\Seeder;\nuse Spatie\\Permission\\Models\\Permission;\nuse Spatie\\Permission\\Models\\Role;\nuse Spatie\\Permission\\PermissionRegistrar;\n\nclass PermissionsDemoSeeder extends Seeder\n{\n    /**\n     * Create the initial roles and permissions.\n     */\n    public function run(): void\n    {\n        // Reset cached roles and permissions\n        app()[PermissionRegistrar::class]->forgetCachedPermissions();\n\n        // create permissions\n        Permission::create(['name' => 'edit articles']);\n        Permission::create(['name' => 'delete articles']);\n        Permission::create(['name' => 'publish articles']);\n        Permission::create(['name' => 'unpublish articles']);\n\n        // create roles and assign existing permissions\n        $role1 = Role::create(['name' => 'writer']);\n        $role1->givePermissionTo('edit articles');\n        $role1->givePermissionTo('delete articles');\n\n        $role2 = Role::create(['name' => 'admin']);\n        $role2->givePermissionTo('publish articles');\n        $role2->givePermissionTo('unpublish articles');\n\n        $role3 = Role::create(['name' => 'Super-Admin']);\n        // gets all permissions via Gate::before rule; see AuthServiceProvider\n\n        // create demo users\n        $user = \\App\\Models\\User::factory()->create([\n            'name' => 'Example User',\n            'email' => 'tester@example.com',\n        ]);\n        $user->assignRole($role1);\n\n        $user = \\App\\Models\\User::factory()->create([\n            'name' => 'Example Admin User',\n            'email' => 'admin@example.com',\n        ]);\n        $user->assignRole($role2);\n\n        $user = \\App\\Models\\User::factory()->create([\n            'name' => 'Example Super-Admin User',\n            'email' => 'superadmin@example.com',\n        ]);\n        $user->assignRole($role3);\n    }\n}\n\n```\n\n- re-migrate and seed the database:\n\n```sh\nphp artisan migrate:fresh --seed --seeder=PermissionsDemoSeeder\n```\n\n### Grant Super-Admin access\nSuper-Admins are a common feature. The following approach allows that when your Super-Admin user is logged in, all permission-checks in your app which call `can()` or `@can()` will return true.\n\n- Create a role named `Super-Admin`. (Or whatever name you wish; but use it consistently just like you must with any role name.)\n- Add a Gate::before check in your `AuthServiceProvider` (or `AppServiceProvider` since Laravel 11):\n\n```diff\n+ use Illuminate\\Support\\Facades\\Gate;\n\n    public function boot()\n    {\n+        // Implicitly grant \"Super-Admin\" role all permission checks using can()\n+        Gate::before(function ($user, $ability) {\n+            if ($user->hasRole('Super-Admin')) {\n+                return true;\n+            }\n+        });\n    }\n```\n\n\n### Application Code\nThe permissions created in the seeder above imply that there will be some sort of Posts or Article features, and that various users will have various access control levels to manage/view those objects.\n\nYour app will have Models, Controllers, routes, Views, Factories, Policies, Tests, middleware, and maybe additional Seeders.\n\nYou can see examples of these in the demo app at https://github.com/drbyte/spatie-permissions-demo/\n\n\n### Quick Examples\nIf you are creating a demo app for reporting a bug or getting help with troubleshooting something, skip this section and proceed to \"Sharing\" below.\n\nIf this is your first app with this package, you may want some quick permission examples to see it in action. If you've set up your app using the instructions above, the following examples will work in conjunction with the users and permissions created in the seeder.\n\nThree users were created: tester@example.com, admin@example.com, superadmin@example.com and the password for each is \"password\".\n\n`/resources/views/dashboard.php`\n```diff\n    <div class=\"p-6 text-gray-900\">\n        {{ __(\"You're logged in!\") }}\n    </div>\n+    @can('edit articles')\n+    You can EDIT ARTICLES.\n+    @endcan\n+    @can('publish articles')\n+    You can PUBLISH ARTICLES.\n+    @endcan\n+    @can('only super-admins can see this section')\n+    Congratulations, you are a super-admin!\n+    @endcan\n```\nWith the above code, when you login with each respective user, you will see different messages based on that access.\n\nHere's a routes example with Breeze and Laravel 11. \nEdit `/routes/web.php`:\n```diff\n-Route::middleware('auth')->group(function () {\n+Route::middleware('role_or_permission:publish articles')->group(function () {\n    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');\n    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');\n    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');\n});\n```\nWith the above change, you will be unable to access the user \"Profile\" page unless you are logged in with \"admin\" or \"super-admin\". You could change `role_or_permission:publish_articles` to `role:writer` to make it only available to the \"test\" user.\n\n## Sharing\nTo share your app on Github for easy collaboration:\n\n- create a new public repository on Github, without any extras like readme/etc.\n- follow github's sample code for linking your local repo and uploading the code. It will look like this:\n\n```sh\ngit remote add origin git@github.com:YOURUSERNAME/REPONAME.git\ngit push -u origin main\n```\nThe above only needs to be done once. \n\n- then add the rest of your code by making new commits:\n\n```sh\ngit add .\ngit commit -m \"Explain what your commit is about here\"\ngit push origin main\n```\nRepeat the above process whenever you change code that you want to share.\n\nThose are the basics!\n"
  },
  {
    "path": "docs/basic-usage/passport.md",
    "content": "---\ntitle: Passport Client Credentials Grant usage\nweight: 12\n---\n\n**NOTE** currently this only works for Laravel 9 and Passport 11 and newer.\n\n## Install Passport\nFirst of all make sure to have Passport installed as described in the [Laravel documentation](https://laravel.com/docs/master/passport).\n\n## Extend the Client model\nAfter installing the Passport package we need to extend Passports Client model. \nThe extended Client model should look like something as shown below.\n\n```php\nuse Illuminate\\Contracts\\Auth\\Access\\Authorizable as AuthorizableContract;\nuse Illuminate\\Foundation\\Auth\\Access\\Authorizable;\nuse Laravel\\Passport\\Client as BaseClient;\nuse Spatie\\Permission\\Traits\\HasRoles;\n\nclass Client extends BaseClient implements AuthorizableContract\n{\n    use HasRoles;\n    use Authorizable;\n\n    public $guard_name = 'api';\n    \n    // or\n    \n    public function guardName()\n    {\n        return 'api';\n    }\n}\n```\n\nYou need to extend the Client model to make it possible to add the required traits and properties/ methods.\nThe extended Client should either provide a `$guard_name` property or a `guardName()` method.\nThey should return a string that matches the [configured](https://laravel.com/docs/master/passport#installation) guard name for the passport driver.\n\nTo tell Passport to use this extended Client, add the rule below to the `boot` method of your `App\\Providers\\AuthServiceProvider` class.\n```php\nPassport::useClientModel(\\App\\Models\\Client::class); // Use the namespace of your extended Client.\n```\n\n## Middleware\nAll middleware provided by this package work with the Client.\n\nDo make sure that you only wrap your routes in the [`client`](https://laravel.com/docs/master/passport#via-middleware) middleware and not the `auth:api` middleware as well.\nWrapping routes in the `auth:api` middleware currently does not work for the Client Credentials Grant.\n\n## Config\nFinally, update the config file as well. Setting `use_passport_client_credentials` to `true` will make sure that the right checks are performed.\n\n```php\n// config/permission.php\n'use_passport_client_credentials' => true,\n```\n"
  },
  {
    "path": "docs/basic-usage/role-permissions.md",
    "content": "---\ntitle: Using Permissions via Roles\nweight: 3\n---\n\n## Assigning Roles\n\nA role can be assigned to any user:\n\n```php\n$user->assignRole('writer');\n\n// You can also assign multiple roles at once\n$user->assignRole('writer', 'admin');\n// or as an array\n$user->assignRole(['writer', 'admin']);\n```\n\nA role can be removed from a user:\n\n```php\n$user->removeRole('writer');\n```\n\nRoles can also be synced:\n\n```php\n// All current roles will be removed from the user and replaced by the array given\n$user->syncRoles(['writer', 'admin']);\n```\n\n## Checking Roles\n\nYou can determine if a user has a certain role:\n\n```php\n$user->hasRole('writer');\n\n// or at least one role from an array of roles:\n$user->hasRole(['editor', 'moderator']);\n```\n\nYou can also determine if a user has any of a given list of roles:\n\n```php\n$user->hasAnyRole(['writer', 'reader']);\n// or\n$user->hasAnyRole('writer', 'reader');\n```\n\nYou can also determine if a user has all of a given list of roles:\n\n```php\n$user->hasAllRoles(Role::all());\n```\n\nYou can also determine if a user has exactly all of a given list of roles:\n\n```php\n$user->hasExactRoles(Role::all());\n```\n\nThe `assignRole`, `hasRole`, `hasAnyRole`, `hasAllRoles`, `hasExactRoles`  and `removeRole` functions can accept a\n string, a `\\Spatie\\Permission\\Models\\Role` object or an `\\Illuminate\\Support\\Collection` object.\n\n\n## Assigning Permissions to Roles\n\nA permission can be given to a role:\n\n```php\n$role->givePermissionTo('edit articles');\n```\n\nYou can determine if a role has a certain permission:\n\n```php\n$role->hasPermissionTo('edit articles');\n```\n\nA permission can be revoked from a role:\n\n```php\n$role->revokePermissionTo('edit articles');\n```\n\nOr revoke & add new permissions in one go:\n\n```php\n$role->syncPermissions(['edit articles', 'delete articles']);\n```\n\nThe `givePermissionTo` and `revokePermissionTo` functions can accept a\nstring or a `Spatie\\Permission\\Models\\Permission` object.\n\n\n**NOTE: Permissions are inherited from roles automatically.**\n\n\n## What Permissions Does A Role Have?\n\nThe `permissions` property on any given role returns a collection with all the related permission objects. This collection can respond to usual Eloquent Collection operations, such as count, sort, etc.\n\n```php\n// get collection\n$role->permissions;\n\n// return only the permission names:\n$role->permissions->pluck('name');\n\n// count the number of permissions assigned to a role\ncount($role->permissions);\n// or\n$role->permissions->count();\n```\n\n## Assigning Direct Permissions To A User\n\nAdditionally, individual permissions can be assigned to the user too. \nFor instance:\n\n```php\n$role = Role::findByName('writer');\n$role->givePermissionTo('edit articles');\n\n$user->assignRole('writer');\n\n$user->givePermissionTo('delete articles');\n```\n\nIn the above example, a role is given permission to edit articles and this role is assigned to a user. \nNow the user can edit articles and additionally delete articles. The permission of 'delete articles' is the user's direct permission because it is assigned directly to them.\nWhen we call `$user->hasDirectPermission('delete articles')` it returns `true`, \nbut `false` for `$user->hasDirectPermission('edit articles')`.\n\nThis method is useful if one builds a form for setting permissions for roles and users in an application and wants to restrict or change inherited permissions of roles of the user, i.e. allowing to change only direct permissions of the user.\n\n\nYou can check if the user has a Specific or All or Any of a set of permissions directly assigned:\n\n```php\n// Check if the user has Direct permission\n$user->hasDirectPermission('edit articles')\n\n// Check if the user has All direct permissions\n$user->hasAllDirectPermissions(['edit articles', 'delete articles']);\n\n// Check if the user has Any permission directly\n$user->hasAnyDirectPermission(['create articles', 'delete articles']);\n```\nBy following the previous example, when we call `$user->hasAllDirectPermissions(['edit articles', 'delete articles'])` \nit returns `false`, because the user does not have `edit articles` as a direct permission.\nWhen we call\n`$user->hasAnyDirectPermission('edit articles')`, it returns `true` because the user has one of the provided permissions.\n\n\nYou can examine all of these permissions:\n\n```php\n// Direct permissions\n$user->getDirectPermissions() // Or $user->permissions;\n\n// Permissions inherited from the user's roles\n$user->getPermissionsViaRoles();\n\n// All permissions which apply on the user (inherited and direct)\n$user->getAllPermissions();\n```\n\nAll these responses are collections of `Spatie\\Permission\\Models\\Permission` objects.\n\nIf we follow the previous example, the first response will be a collection with the `delete article` permission and \nthe second will be a collection with the `edit article` permission and the third will contain both.\n\n"
  },
  {
    "path": "docs/basic-usage/super-admin.md",
    "content": "---\ntitle: Defining a Super-Admin\nweight: 8\n---\n\nWe strongly recommend that a Super-Admin be handled by setting a global `Gate::before` or `Gate::after` rule which checks for the desired role. \n\nThen you can implement the best-practice of primarily using permission-based controls (@can and $user->can, etc) throughout your app, without always having to check for \"is this a super-admin\" everywhere. **Best not to use role-checking (ie: `hasRole`) (except here in Gate/Policy rules) when you have Super Admin features like this.**\n\n## Gate::before/Policy::before vs HasPermissionTo / HasAnyPermission / HasDirectPermission / HasAllPermissions\nIMPORTANT:\nThe Gate::before is the best approach for Super-Admin functionality, and aligns well with the described \"Best Practices\" of using roles as a way of grouping permissions, and assigning that access to Users. Using this approach, you can/must call Laravel's standard `can()`, `canAny()`, `cannot()`, etc checks for permission authorization to get a correct Super response. \n\n### HasPermissionTo, HasAllPermissions, HasAnyPermission, HasDirectPermission\nCalls to this package's internal API which bypass Laravel's Gate (such as a direct call to `->hasPermissionTo()`) will not go through the Gate, and thus will not get the Super response, unless you have actually added that specific permission to the Super-Admin \"role\".\n\nThe only reason for giving specific permissions to a Super-Admin role is if you intend to call the `has` methods directly instead of the Gate's `can()` methods.\n\n\n## `Gate::before`\nIf you want a \"Super Admin\" role to respond `true` to all permissions, without needing to assign all those permissions to a role, you can use [Laravel's `Gate::before()` method](https://laravel.com/docs/master/authorization#intercepting-gate-checks). For example:\n\n`/app/Providers/AppServiceProvider`:\n```php\nuse Illuminate\\Support\\Facades\\Gate;\n// ...\npublic function boot(): void\n{\n    // Implicitly grant \"Super Admin\" role all permissions\n    // This works in the app by using gate-related functions like auth()->user->can() and @can()\n    Gate::before(function ($user, $ability) {\n        return $user->hasRole('Super Admin') ? true : null;\n    });\n}\n```\n\nNOTE: `Gate::before` rules need to return `null` rather than `false`, else it will interfere with normal policy operation. [See more.](https://laracasts.com/discuss/channels/laravel/policy-gets-never-called#reply=492526)\n\nJeffrey Way explains the concept of a super-admin (and a model owner, and model policies) in the [Laravel 6 Authorization Filters](https://laracasts.com/series/laravel-6-from-scratch/episodes/51) video and some related lessons in that chapter.\n\n## Policy `before()`\n\nIf you aren't using `Gate::before()` as described above, you could alternatively grant super-admin control by checking the role in individual Policy classes, using the `before()` method.\n\nHere is an example from the [Laravel Documentation on Policy Filters](https://laravel.com/docs/master/authorization#policy-filters), where you can define `before()` in your Policy where needed:\n\n```php\nuse App\\Models\\User; // could be any Authorizable model\n\n/**\n * Perform pre-authorization checks on the model.\n */\npublic function before(User $user, string $ability): ?bool\n{\n    if ($user->hasRole('Super Admin')) {\n        return true;\n    }\n \n    return null; // see the note above in Gate::before about why null must be returned here.\n}\n```\n\n## `Gate::after`\n\nAlternatively you might want to move the Super Admin check to the `Gate::after` phase instead, particularly if your Super Admin shouldn't be allowed to do things your app doesn't want \"anyone\" to do, such as writing more than 1 review, or bypassing unsubscribe rules, etc.\n\nThe following code snippet is inspired from [Freek's blog article](https://freek.dev/1325-when-to-use-gateafter-in-laravel) where this topic is discussed further. You can also consult the [Laravel Docs on gate interceptions](https://laravel.com/docs/master/authorization#intercepting-gate-checks)\n\n```php\n// somewhere in a service provider\n\nGate::after(function ($user, $ability) {\n   return $user->hasRole('Super Admin'); // note this returns boolean\n});\n```\n"
  },
  {
    "path": "docs/basic-usage/teams-permissions.md",
    "content": "---\ntitle: Teams permissions\nweight: 5\n---\n\nWhen enabled, teams permissions offers you flexible control for a variety of scenarios. The idea behind teams permissions is inspired by the default permission implementation of [Laratrust](https://laratrust.santigarcor.me/).\n\n## Enabling Teams Permissions Feature\n\nNOTE: These configuration changes must be made **before** performing the migration when first installing the package.\n\nIf you have already run the migration and want to upgrade your implementation, you can run the artisan console command `php artisan permission:setup-teams`, to create a new migration file named [`xxxx_xx_xx_xx_add_teams_fields.php`](https://github.com/spatie/laravel-permission/blob/main/database/migrations/add_teams_fields.php.stub) and then run `php artisan migrate` to upgrade your database tables.\n\nTeams permissions can be enabled in the permission config file:\n\n```php\n// config/permission.php\n'teams' => true,\n```\n\nAlso, if you want to use a custom foreign key for teams you set it in the permission config file:\n```php\n// config/permission.php\n'team_foreign_key' => 'custom_team_id',\n```\n\n## Working with Teams Permissions\n\nAfter implementing a solution for selecting a team on the authentication process \n(for example, setting the `team_id` of the currently selected team on the **session**: `session(['team_id' => $team->team_id]);` ), \nwe can set global `team_id` from anywhere, but works better if you create a `Middleware`. \n\nExample Team Middleware:\n\n```php\nnamespace App\\Http\\Middleware;\n\nclass TeamsPermission\n{\n    public function handle($request, \\Closure $next){\n        if(!empty(auth()->user())){\n            // session value set on login\n            setPermissionsTeamId(session('team_id'));\n        }\n        // other custom ways to get team_id\n        /*if(!empty(auth('api')->user())){\n            // `getTeamIdFromToken()` example of custom method for getting the set team_id \n            setPermissionsTeamId(auth('api')->user()->getTeamIdFromToken());\n        }*/\n        \n        return $next($request);\n    }\n}\n```\n\n**YOU MUST ALSO** set [the `$middlewarePriority` array](https://laravel.com/docs/master/middleware#sorting-middleware) in `app/Http/Kernel.php` to include your custom middleware before the `SubstituteBindings` middleware, else you may get *404 Not Found* responses when a *403 Not Authorized* response might be expected.\n\nFor example you can add something similar to the `boot` method of your `AppServiceProvider`:\n\n```php\nuse App\\Http\\Middleware\\YourCustomMiddlewareClass;\nuse Illuminate\\Foundation\\Http\\Kernel;\nuse Illuminate\\Routing\\Middleware\\SubstituteBindings;\nuse Illuminate\\Support\\ServiceProvider;\n\nclass AppServiceProvider extends ServiceProvider\n{\n    public function register(): void\n    {\n        //\n    }\n\n    public function boot(): void\n    {\n        /** @var Kernel $kernel */\n        $kernel = app()->make(Kernel::class);\n\n        $kernel->addToMiddlewarePriorityBefore(\n            YourCustomMiddlewareClass::class,\n            SubstituteBindings::class,\n        );\n    }\n}\n```\n### Using LiveWire? \n\nYou may need to register your team middleware as Persisted in Livewire. See [Livewire docs: Configuring Persistent Middleware](https://livewire.laravel.com/docs/security#configuring-persistent-middleware)\n\n## Roles Creating\n\nWhen creating a role you can pass the `team_id` as an optional parameter\n \n```php\n// with null team_id it creates a global role; global roles can be assigned to any team and they are unique\nRole::create(['name' => 'writer', 'team_id' => null]);\n\n// creates a role with team_id = 1; team roles can have the same name on different teams\nRole::create(['name' => 'reader', 'team_id' => 1]);\n\n// creating a role without team_id makes the role take the default global team_id\nRole::create(['name' => 'reviewer']);\n```\n\n## Roles/Permissions Assignment and Removal\n\nThe role/permission assignment and removal for teams are the same as without teams, but they take the global `team_id` which is set on login.\n\n## Changing The Active Team ID\n\nWhile your middleware will set a user's `team_id` upon login, you may later need to set it to another team for various reasons. The two most common reasons are these:\n\n### Switching Teams After Login\nIf your application allows the user to switch between various teams which they belong to, you can activate the roles/permissions for that team by calling `setPermissionsTeamId($new_team_id)` and unsetting relations as described below.\n\n### Administrating Team Details\nYou may have created a User-Manager page where you can view the roles/permissions of users on certain teams. For managing that user in each team they belong to, you must also use `setPermissionsTeamId($new_team_id)` to cause lookups to relate to that new team, and unset prior relations as described below.\n\n### Querying Roles/Permissions for Other Teams\nWhenever you switch the active `team_id` using `setPermissionsTeamId()`, you need to `unset` the user's/model's `roles` and `permissions` relations before querying what roles/permissions that user has (`$user->roles`, etc) and before calling any authorization functions (`can()`, `hasPermissionTo()`, `hasRole()`, etc).\n\nExample:\n```php\n// set active global team_id\nsetPermissionsTeamId($new_team_id);\n\n// $user = Auth::user();\n\n// unset cached model relations so new team relations will get reloaded\n$user->unsetRelation('roles')->unsetRelation('permissions');\n\n// Now you can check:\n$roles = $user->roles;\n$hasRole = $user->hasRole('my_role');\n$user->hasPermissionTo('foo');\n$user->can('bar');\n// etc\n```\n\n## Defining a Super-Admin on Teams\n\nGlobal roles can be assigned to different teams, and `team_id` (which is the primary key of the relationships) is always required. \n\nIf you want a \"Super Admin\" global role for a user, when you create a new team you must assign it to your user. Example:\n\n```php\nnamespace App\\Models;\n\nclass YourTeamModel extends \\Illuminate\\Database\\Eloquent\\Model\n{\n    // ...\n    public static function boot()\n    {\n        parent::boot();\n\n        // here assign this team to a global user with global default role\n        self::created(function ($model) {\n           // temporary: get session team_id for restore at end\n           $session_team_id = getPermissionsTeamId();\n           // set actual new team_id to package instance\n           setPermissionsTeamId($model);\n           // get the admin user and assign roles/permissions on new team model\n           User::find('your_user_id')->assignRole('Super Admin');\n           // restore session team_id to package instance using temporary value stored above\n           setPermissionsTeamId($session_team_id);\n        });\n    }\n    // ...\n}\n```\n"
  },
  {
    "path": "docs/basic-usage/wildcard-permissions.md",
    "content": "---\ntitle: Wildcard permissions\nweight: 6\n---\n\nWhen enabled, wildcard permissions offers you a flexible representation for a variety of permission schemes. \n\nThe wildcard permissions implementation is inspired by the default permission implementation of \n [Apache Shiro](https://shiro.apache.org/permissions.html). See the Shiro documentation for more examples and deeper explanation of the concepts.\n\n## Enabling Wildcard Features\n\nWildcard permissions can be enabled in the permission config file:\n\n```php\n// config/permission.php\n'enable_wildcard_permission' => true,\n```\n\n## Wildcard Syntax\n\nA wildcard permission string is made of one or more parts separated by dots (.).\n\n```php\n$permission = 'posts.create.1';\n```\n\nThe meaning of each part of the string depends on the application layer. \n\n> You can use as many parts as you like. So you are not limited to the three-tiered structure, even though \nthis is the common use-case, representing `{resource}.{action}.{target}`.\n\n> **NOTE: You must actually create the wildcarded permissions** (eg: `posts.create.1`) before you can assign them or check for them.\n\n> **NOTE: You must create any wildcard permission patterns** (eg: `posts.create.*`) before you can assign them or check for them.\n\n## Using Wildcards\n\n> ALERT: The `*` means \"ALL\". It does **not** mean \"ANY\".\n\nEach part can also contain wildcards (`*`). So let's say we assign the following permission to a user:\n\n```php\nPermission::create(['name'=>'posts.*']);\n$user->givePermissionTo('posts.*');\n// is the same as\nPermission::create(['name'=>'posts']);\n$user->givePermissionTo('posts');\n```\n\nGiven the example above, everyone who is assigned to this permission will be allowed every action on posts. It is not necessary to use a \nwildcard on the last part of the string. This is automatically assumed.\n\n```php\n// will be true\n$user->can('posts.create');\n$user->can('posts.edit');\n$user->can('posts.delete');\n```\n(Note that the `posts.create` and `posts.edit` and `posts.delete` permissions must also be created.)\n\n## Meaning of the * Asterisk\n\nThe `*` means \"ALL\". It does **not** mean \"ANY\".\n\nThus `can('post.*')` will only pass if the user has been assigned `post.*` explicitly, and the `post.*` Permission has been created.\n\n\n## Subparts\n\nBesides the use of parts and wildcards, subparts can also be used. Subparts are divided with commas (,). This is a \npowerful feature that lets you create complex permission schemes.\n\n```php\n// user can only do the actions create, update and view on both resources posts and users\nPermission::create(['name'=>'posts,users.create,update,view']);\n$user->givePermissionTo('posts,users.create,update,view');\n\n// user can do the actions create, update, view on any available resource\nPermission::create(['name'=>'*.create,update,view']);\n$user->givePermissionTo('*.create,update,view');\n\n// user can do any action on posts with ids 1, 4 and 6 \nPermission::create(['name'=>'posts.*.1,4,6']);\n$user->givePermissionTo('posts.*.1,4,6');\n```\n\n> Remember: the meaning of each 'part' is determined by your application! So, you are free to use each part as you like. And you can use as many parts and subparts as you want.\n"
  },
  {
    "path": "docs/best-practices/_index.md",
    "content": "---\ntitle: Best Practices\nweight: 2\n---\n"
  },
  {
    "path": "docs/best-practices/performance.md",
    "content": "---\ntitle: Performance Tips\nweight: 10\n---\n\nOn small apps, most of the following will be moot, and unnecessary.\n\nOften we think in terms of \"roles have permissions\" so we lookup a Role, and call `$role->givePermissionTo()` \nto indicate what users with that role are allowed to do. This is perfectly fine!\n\nAnd yet, in some situations, particularly if your app is deleting and adding new permissions frequently,\nyou may find that things are more performant if you lookup the permission and assign it to the role, like: \n`$permission->assignRole($role)`.\nThe end result is the same, but sometimes it runs quite a lot faster.\n\nAlso, because of the way this package enforces some protections for you, on large databases you may find\nthat instead of creating permissions with:\n```php\nPermission::create([attributes]);\n```\nit might be faster (more performant) to use:\n```php\n$permission = Permission::make([attributes]); \n$permission->saveOrFail();\n```\n\nAs always, if you choose to bypass the provided object methods for adding/removing/syncing roles and permissions \nby manipulating Role and Permission objects directly in the database,\n**you will need to manually reset the package cache** with the PermissionRegistrar's method for that,\nas described in the Cache section of the docs.\n"
  },
  {
    "path": "docs/best-practices/roles-vs-permissions.md",
    "content": "---\ntitle: Roles vs Permissions\nweight: 1\n---\n\nBest-Practice for thinking about Roles vs Permissions is this:\n\n**Roles** are best to only assign to **Users** in order to \"**group**\" people by \"**sets of permissions**\".\n\n**Permissions** are best assigned **to roles**. \nThe more granular/detailed your permission-names (such as separate permissions like \"view document\" and \"edit document\"), the easier it is to control access in your application.\n\n**Users** should *rarely* be given \"direct\" permissions. Best if Users inherit permissions via the Roles that they're assigned to.\n\nWhen designed this way, all the sections of your application can check for specific permissions needed to access certain features or perform certain actions AND this way you can always **use the native Laravel `@can` and `can()` directives everywhere** in your app, which allows Laravel's Gate layer to do all the heavy lifting.  \n\nExample: it's safer to have your Views test `@can('view member addresses')` or `@can('edit document')`, INSTEAD of testing for `$user->hasRole('Editor')`. It's easier to control displaying a \"section\" of content vs edit/delete buttons if you have \"view document\" and \"edit document\" permissions defined. And then Writer role would get both \"view\" and \"edit\" assigned to it. And then the user would get the Writer role.\n\nThis also allows you to treat permission names as static (only editable by developers), and then your application (almost) never needs to know anything about role names, so you could (almost) change role names at will.\n\nSummary:\n- **users** have `roles`\n- **roles** have `permissions`\n- app always checks for `permissions` (as much as possible), not `roles`\n- **views** check permission-names\n- **policies** check permission-names\n- **model policies** check permission-names\n- **controller methods** check permission-names\n- **middleware** check permission names, or sometimes role-names\n- **routes** check permission-names, or maybe role-names if you need to code that way.\n\nSometimes certain groups of `route` rules may make best sense to group them around a `role`, but still, whenever possible, there is less overhead used if you can check against a specific `permission` instead.\n\n\n### FURTHER READING:\n\n[@joelclermont](https://github.com/joelclermont) at [masteringlaravel.io](https://masteringlaravel.io/daily) offers similar guidance in his post about [Treating Feature Access As Data, Not Code](https://masteringlaravel.io/daily/2025-01-09-treat-feature-access-as-data-not-code)\n"
  },
  {
    "path": "docs/best-practices/using-policies.md",
    "content": "---\ntitle: Model Policies\nweight: 2\n---\n\nThe best way to incorporate access control for application features is with [Laravel's Model Policies](https://laravel.com/docs/authorization#creating-policies).\n\nUsing Policies allows you to simplify things by abstracting your \"control\" rules into one place, where your application logic can be combined with your permission rules.\n\nJeffrey Way explains the concept simply in the [Laravel 6 Authorization Filters](https://laracasts.com/series/laravel-6-from-scratch/episodes/51) and [policies](https://laracasts.com/series/laravel-6-from-scratch/episodes/63) videos and in other related lessons in that chapter. He also mentions how to set up a super-admin, both in a model policy and globally in your application.\n\nHere's a partial example of a PostPolicy which could control access to Post model records:\n```php\n<?php\nnamespace App\\Policies;\n\nuse App\\Models\\Post;\nuse App\\Models\\User;\nuse Illuminate\\Auth\\Access\\HandlesAuthorization;\n\nclass PostPolicy\n{\n    use HandlesAuthorization;\n\n    public function view(?User $user, Post $post): bool\n    {\n        if ($post->published) {\n            return true;\n        }\n\n        // visitors cannot view unpublished items\n        if ($user === null) {\n            return false;\n        }\n\n        // admin overrides published status\n        if ($user->can('view unpublished posts')) {\n            return true;\n        }\n\n        // authors can view their own unpublished posts\n        return $user->id == $post->user_id;\n    }\n\n    public function create(User $user): bool\n    {\n        return $user->can('create posts');\n    }\n\n    public function update(User $user, Post $post): bool\n    {\n        if ($user->can('edit all posts')) {\n            return true;\n        }\n\n        if ($user->can('edit own posts')) {\n            return $user->id == $post->user_id;\n        }\n    }\n\n    public function delete(User $user, Post $post): bool\n    {\n        if ($user->can('delete any post')) {\n            return true;\n        }\n\n        if ($user->can('delete own posts')) {\n            return $user->id == $post->user_id;\n        }\n    }\n}\n```\n"
  },
  {
    "path": "docs/changelog.md",
    "content": "---\ntitle: Changelog\nweight: 10\n---\n\nAll notable changes to laravel-permission are documented [on GitHub](https://github.com/spatie/laravel-permission/blob/main/CHANGELOG.md)\n"
  },
  {
    "path": "docs/installation-laravel.md",
    "content": "---\ntitle: Installation in Laravel\nweight: 4\n---\n\n## Laravel Version Compatibility\n\nSee the \"Prerequisites\" documentation page for compatibility details.\n\n## Installing\n\n1. Consult the **Prerequisites** page for important considerations regarding your **User** models!\n\n2. This package **publishes a `config/permission.php` file**. If you already have a file by that name, you must rename or remove it.\n\n3. You can **install the package via composer**:\n\n        composer require spatie/laravel-permission\n\n4. The Service Provider will automatically be registered; however, if you wish to manually register it, you can manually add the `Spatie\\Permission\\PermissionServiceProvider::class` service provider to the array in `bootstrap/providers.php`.\n\n\n5. **You should publish** [the migration](https://github.com/spatie/laravel-permission/blob/main/database/migrations/create_permission_tables.php.stub) and the [`config/permission.php` config file](https://github.com/spatie/laravel-permission/blob/main/config/permission.php) with:\n\n    ```\n    php artisan vendor:publish --provider=\"Spatie\\Permission\\PermissionServiceProvider\"\n    ```\n\n6. BEFORE RUNNING MIGRATIONS\n\n   - **If you are using UUIDs**, see the Advanced section of the docs on UUID steps, before you continue. It explains some changes you may want to make to the migrations and config file before continuing. It also mentions important considerations after extending this package's models for UUID capability.\n\n   - **If you are going to use the TEAMS features** you must update your [`config/permission.php` config file](https://github.com/spatie/laravel-permission/blob/main/config/permission.php):\n       - must set `'teams' => true,`\n       - and (optional) you may set `team_foreign_key` name in the config file if you want to use a custom foreign key in your database for teams\n\n   - **If you are using MySQL 8+**, look at the **Prerequisites** docs page for notes about MySQL 8+ to set/limit the index key length, and edit accordingly. If you get `ERROR: 1071 Specified key was too long` then you need to do this.\n\n   - **If you are using CACHE_STORE=database**, be sure to [install Laravel's cache migration](https://laravel.com/docs/cache#prerequisites-database), else you will encounter cache errors.\n\n7. **Clear your config cache**. This package requires access to the `permission` config settings in order to run migrations. If you've been caching configurations locally, clear your config cache with either of these commands:\n\n        php artisan optimize:clear\n        # or\n        php artisan config:clear\n\n8. **Run the migrations**: After the config and migration have been published and configured, you can create the tables for this package by running:\n\n        php artisan migrate\n\n9. **Add the necessary trait to your User model**: \n\n        // The User model requires this trait\n        use HasRoles;\n\n10. Consult the **Basic Usage** section of the docs to get started using the features of this package.\n\n.\n\n\n## Default config file contents\n\nYou can view the default config file contents at:\n\n[https://github.com/spatie/laravel-permission/blob/main/config/permission.php](https://github.com/spatie/laravel-permission/blob/main/config/permission.php)\n"
  },
  {
    "path": "docs/introduction.md",
    "content": "---\ntitle: Introduction\nweight: 1\n---\n\nThis package allows you to manage user permissions and roles in a database.\n\nOnce installed you can do stuff like this:\n\n```php\n// Adding permissions to a user\n$user->givePermissionTo('edit articles');\n\n// Adding permissions via a role\n$user->assignRole('writer');\n\n$role->givePermissionTo('edit articles');\n```\n\nIf you're using multiple guards we've got you covered as well. Every guard will have its own set of permissions and roles that can be assigned to the guard's users. Read about it in the [using multiple guards](./basic-usage/multiple-guards/) section.\n\nBecause all permissions will be registered on [Laravel's gate](https://laravel.com/docs/authorization), you can check if a user has a permission with Laravel's default `can` function:\n\n```php\n$user->can('edit articles');\n```\n\nand Blade directives:\n\n```blade\n@can('edit articles')\n...\n@endcan\n```\n"
  },
  {
    "path": "docs/prerequisites.md",
    "content": "---\ntitle: Prerequisites\nweight: 3\n---\n\n## Laravel Version Compatibility\n\nLaravel Version | Package Version\n----------------|-----------\n 12, 13         |  `^7.0`  (PHP 8.3+)\n 8,9,10,11,12   |  `^6.0`  (PHP 8.0+)\n 7,8,9,10       |  `^5.8`\n 7,8,9          |  `^5.7`\n 7,8            |  `^5.4`-`^5.6`\n 6,7,8          |  `^5.0`-`^5.3`\n 6,7,8          |  `^4`\n 5.8            |  `^3`\n   \n## User Model / Contract/Interface\n\nThis package uses Laravel's Gate layer to provide Authorization capabilities.\nThe Gate/authorization layer requires that your `User` model implement the `Illuminate\\Contracts\\Auth\\Access\\Authorizable` contract. \nOtherwise the `can()` and `authorize()` methods will not work in your controllers, policies, templates, etc.\n\nIn the `Installation` instructions you'll see that the `HasRoles` trait must be added to the User model to enable this package's features.\n\nThus, a typical basic User model would have these basic minimum requirements:\n\n```php\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Spatie\\Permission\\Traits\\HasRoles;\n\nclass User extends Authenticatable\n{\n    use HasRoles;\n\n    // ...\n}\n```\n\n## Must not have a [role] or [roles] property/relation, nor a [roles()] method\n\nYour `User` model/object MUST NOT have a `role` or `roles` property (or field in the database by that name), nor a `roles()` method on it (nor a `roles` relation). Those will interfere with the properties and methods and relations added by the `HasRoles` trait provided by this package, thus causing unexpected outcomes when this package's methods are used to inspect roles and permissions.\n\n## Must not have a [permission] or [permissions] property/relation, nor a [permissions()] method\n\nYour `User` model/object MUST NOT have a `permission` or `permissions` property (or field in the database by that name), nor a `permissions()` method on it (nor a `permissions` relation). Those will interfere with the properties and methods and relations added by the `HasPermissions` trait provided by this package (which is invoked via the `HasRoles` trait).\n\n## Config file\n\nThis package publishes a `config/permission.php` file. If you already have a file by that name, you must rename or remove it, as it will conflict with this package. You could optionally merge your own values with those required by this package, as long as the keys that this package expects are present. See the source file for more details.\n\n## Database Schema Limitations\n\nPotential error message: \"1071 Specified key was too long; max key length is 1000 bytes\"\n\nMySQL 8.0+ limits index key lengths, which might be too short for some compound indexes used by this package.\nThis package publishes a migration which combines multiple columns in a single index. With `utf8mb4` the 4-bytes-per-character requirement of `mb4` means the total length of the columns in the hybrid index can only be `25%` of that maximum index length.\n\n- MyISAM tables limit the index to 1000 characters (which is only 250 total chars in `utf8mb4`)\n- InnoDB tables using ROW_FORMAT of 'Redundant' or 'Compact' limit the index to 767 characters (which is only 191 total chars in `utf8mb4`)\n- InnoDB tables using ROW_FORMAT of 'Dynamic' or 'Compressed' have a 3072 character limit (which is 768 total chars in `utf8mb4`).\n\nDepending on your MySQL or MariaDB configuration, you may implement one of the following approaches:\n\n1. Ideally, configure the database to use InnoDB by default, and use ROW FORMAT of 'Dynamic' by default for all new tables. (See [MySQL](https://dev.mysql.com/doc/refman/8.0/en/innodb-limits.html) and [MariaDB](https://mariadb.com/kb/en/innodb-dynamic-row-format/) docs.)\n\n2. OR if your app doesn't require a longer default, in your AppServiceProvider you can set `Schema::defaultStringLength(125)`. [See the Laravel Docs for instructions](https://laravel.com/docs/10.x/migrations#index-lengths-mysql-mariadb). This will have Laravel set all strings to 125 characters by default.\n\n3. OR you could edit the migration and specify a shorter length for 4 fields. Then in your app be sure to manually impose validation limits on any form fields related to these fields. \nIn the migration file, there are 2 places where you can explicitly set the length for both the 'name' and 'guard_name' fields, on 2 tables. Suggested specific lengths are shown below:\n```php\n    $table->string('name');       // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)\n    $table->string('guard_name'); // For MyISAM use string('guard_name', 25);\n```\n\n## Note for apps using UUIDs/ULIDs/GUIDs\n\nThis package expects the primary key of your `User` model to be an auto-incrementing `int`. If it is not, you may need to modify the `create_permission_tables` migration and/or modify the default configuration. See [https://spatie.be/docs/laravel-permission/advanced-usage/uuid](https://spatie.be/docs/laravel-permission/advanced-usage/uuid) for more information. \n\n## Database foreign-key relationship support\n\nTo enforce database integrity, this package uses foreign-key relationships with cascading deletes. This prevents data mismatch situations if database records are manipulated outside of this package. If your database engine does not support foreign-key relationships, then you will have to alter the migration files accordingly.\n\nThis package does its own detaching of pivot records when deletes are called using provided package methods, so if your database does not support foreign keys then as long as you only use method calls provided by this package for managing related records, there should not be data integrity issues.\n"
  },
  {
    "path": "docs/questions-issues.md",
    "content": "---\ntitle: Questions and issues\nweight: 9\n---\n\nFind yourself stuck using the package? Found a bug? Do you have general questions or suggestions for improving the package? Feel free to [create an issue on GitHub](https://github.com/spatie/laravel-permission/issues), we'll try to address it as soon as possible.\n\nIf you've found a bug regarding security please mail [freek@spatie.be](mailto:freek@spatie.be) instead of using the issue tracker.\n"
  },
  {
    "path": "docs/support-us.md",
    "content": "---\ntitle: Support us\nweight: 2\n---\n\nWe invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us). \n\nWe highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).\n"
  },
  {
    "path": "docs/upgrading.md",
    "content": "---\ntitle: Upgrading\nweight: 6\n---\n\n## Upgrade Essentials\n\nALL upgrades of this package should follow these steps:\n\n1. Composer. Upgrading between major versions of this package always requires the usual Composer steps:\n   - Update your `composer.json` to specify the new major version, for example: `^6.0`\n   - Then run `composer update spatie/laravel-permission`. \n\n2. Migrations. Compare the `migration` file stubs in the NEW version of this package against the migrations you've already run inside your app. If necessary, create a new migration (by hand) to apply any new database changes.\n\n3. Config file. Incorporate any changes to the permission.php config file, updating your existing file. (It may be easiest to make a backup copy of your existing file, re-publish it from this package, and then re-make your customizations to it.)\n\n4. Models. If you have made any custom Models by extending them into your own app, compare the package's old and new models and apply any relevant updates to your custom models.\n\n5. Custom Methods/Traits. If you have overridden any methods from this package's Traits, compare the old and new traits, and apply any relevant updates to your overridden methods.\n\n6. Contract/Interface updates. If you have implemented this package's contracts in any models, check to see if there were any changes to method signatures. Mismatches will trigger PHP errors.\n\n7. Apply any version-specific special updates as outlined below...\n\n8. Review the changelog, which details all the changes: [CHANGELOG](https://github.com/spatie/laravel-permission/blob/main/CHANGELOG.md)\nand/or consult the [Release Notes](https://github.com/spatie/laravel-permission/releases)\n\n\n## Upgrading from v6 to v7\n\nFor guidance with upgrading your extended models, your migrations, your routes, etc, see the **Upgrade Essentials** section at the top of this file.\n\n### Requirements\n\n- PHP 8.3 or higher\n- Laravel 12 or higher\n\n### Service Provider\n\nThe service provider now extends `PackageServiceProvider` from `spatie/laravel-package-tools`. If you have published or extended the service provider, update your references accordingly.\n\nLumen support has been removed.\n\n### Event Class Renames\n\nAll event classes now have an `Event` suffix:\n\n| v6 | v7 |\n|---|---|\n| `Spatie\\Permission\\Events\\PermissionAttached` | `Spatie\\Permission\\Events\\PermissionAttachedEvent` |\n| `Spatie\\Permission\\Events\\PermissionDetached` | `Spatie\\Permission\\Events\\PermissionDetachedEvent` |\n| `Spatie\\Permission\\Events\\RoleAttached` | `Spatie\\Permission\\Events\\RoleAttachedEvent` |\n| `Spatie\\Permission\\Events\\RoleDetached` | `Spatie\\Permission\\Events\\RoleDetachedEvent` |\n\nUpdate any event listeners that reference these classes.\n\n### Command Class Renames\n\nAll command classes now have a `Command` suffix:\n\n| v6 | v7 |\n|---|---|\n| `Spatie\\Permission\\Commands\\CacheReset` | `Spatie\\Permission\\Commands\\CacheResetCommand` |\n| `Spatie\\Permission\\Commands\\CreateRole` | `Spatie\\Permission\\Commands\\CreateRoleCommand` |\n| `Spatie\\Permission\\Commands\\CreatePermission` | `Spatie\\Permission\\Commands\\CreatePermissionCommand` |\n| `Spatie\\Permission\\Commands\\Show` | `Spatie\\Permission\\Commands\\ShowCommand` |\n| `Spatie\\Permission\\Commands\\UpgradeForTeams` | `Spatie\\Permission\\Commands\\UpgradeForTeamsCommand` |\n| `Spatie\\Permission\\Commands\\AssignRole` | `Spatie\\Permission\\Commands\\AssignRoleCommand` |\n\nThe artisan command signatures remain unchanged.\n\n### Removed Deprecated Methods\n\n- `PermissionRegistrar::clearClassPermissions()` has been removed. Use `clearPermissionsCollection()` instead.\n\n### Type Hints\n\nReturn types and parameter types have been added throughout the codebase. If you have extended any of the following classes or traits, you may need to update your method signatures:\n\n- `HasPermissions` trait: `givePermissionTo()`, `syncPermissions()`, `revokePermissionTo()` now return `static`\n- `HasRoles` trait: `assignRole()`, `removeRole()`, `syncRoles()` now return `static`\n- Exception factory methods now return `static` instead of `self`\n- `PermissionRegistrar::setPermissionClass()` and `setRoleClass()` now return `static`\n- `PermissionRegistrar::forgetCachedPermissions()` now returns `bool`\n- `Contracts\\PermissionsTeamResolver::setPermissionsTeamId()` now has typed parameter `int|string|Model|null $id`\n- `Contracts\\Role::hasPermissionTo()` now has typed parameter and optional `$guardName`\n\n### Wildcard Contract\n\nThe `__construct(Model $record)` method has been removed from the `Spatie\\Permission\\Contracts\\Wildcard` interface. If you implement this contract, you can remove the constructor from the interface requirement (your concrete class should still accept a `Model` in its constructor).\n\n\n## Upgrading from v5 to v6\nThere are a few breaking-changes when upgrading to v6, but most of them won't affect you unless you have been customizing things.\n\nFor guidance with upgrading your extended models, your migrations, your routes, etc, see the **Upgrade Essentials** section at the top of this file.\n\n1. Due to the improved ULID/UUID/GUID support, any package methods which accept a Permission or Role `id` must pass that `id` as an `integer`. If you pass it as a numeric string, the functions will attempt to look up the role/permission as a string. In such cases, you may see errors such as `There is no permission named '123' for guard 'web'.` (where `'123'` is being treated as a string because it was passed as a string instead of as an integer). This also applies to arrays of id's: if it's an array of strings we will do a lookup on the name instead of on the id. **This will mostly only affect UI pages** because an HTML Request is received as string data. **The solution is simple:** if you're passing integers to a form field, then convert them back to integers when using that field's data for calling functions to grant/assign/sync/remove/revoke permissions and roles. One way to convert an array of permissions `id`'s from strings to integers is: `collect($validated['permission'])->map(fn($val)=>(int)$val)`\n\n2. If you have overridden the `getPermissionClass()` or `getRoleClass()` methods or have custom Models, you will need to revisit those customizations. See PR #2368 for details. \neg: if you have a custom model you will need to make changes, including accessing the model using `$this->permissionClass::` syntax (eg: using `::` instead of `->`) in all the overridden methods that make use of the models.\n\n    Be sure to compare your custom models with the originals to see what else may have changed.\n\n3. Model and Contract/Interface updates. The Role and Permission Models and Contracts/Interfaces have been updated with syntax changes to method signatures. Update any models you have extended, or contracts implemented, accordingly. See PR [#2380](https://github.com/spatie/laravel-permission/pull/2380) and [#2480](https://github.com/spatie/laravel-permission/pull/2480) for some of the specifics. \n\n4. Migrations WILL need to be upgraded. (They have been updated to anonymous-class syntax that was introduced in Laravel 8, AND some structural coding changes in the registrar class changed the way we extracted configuration settings in the migration files.) There are no changes to the package's structure since v5, so if you had not customized it from the original then replacing the contents of the file should be enough. (Usually, the only customization is if you've switched to UUIDs or customized MySQL index name lengths.)\n**If you get the following error, it means your migration file needs upgrading: `Error: Access to undeclared static property Spatie\\Permission\\PermissionRegistrar::$pivotPermission`**\n\n5. MIDDLEWARE:\n\n    1. The `\\Spatie\\Permission\\Middlewares\\` namespace has been renamed to `\\Spatie\\Permission\\Middleware\\` (singular). Update any references to them in your `/app/Http/Kernel.php` and any routes (or imported classes in your routes files) that have the fully qualified namespace.\n\n    2. NOTE: For consistency with `PermissionMiddleware`, the `RoleOrPermissionMiddleware` has switched from only checking permissions provided by this package to using `canAny()` to check against any abilities registered by your application. This may have the effect of granting those other abilities (such as Super Admin) when using the `RoleOrPermissionMiddleware`, which previously would have failed silently.\n\n    3. In the unlikely event that you have customized the Wildcard Permissions feature by extending the `WildcardPermission` model, please note that the public interface has changed significantly and you will need to update your extended model with the new method signatures.\n\n6. Test suites. If you have tests that manually clear the permission cache and re-register permissions, you no longer need to call `\\Spatie\\Permission\\PermissionRegistrar::class)->registerPermissions();`. In fact, **calls to `->registerPermissions()` MUST be deleted from your tests**. \n    \n    (Calling `app()[\\Spatie\\Permission\\PermissionRegistrar::class]->forgetCachedPermissions();` after creating roles and permissions in migrations and factories and seeders is still okay and encouraged.) \n\n\n## Upgrading from v4 to v5\n\nFollow the instructions described in \"Essentials\" above.\n\n## Upgrading from v3 to v4\n\nUpdate `composer.json` as described in \"Essentials\" above.\n\n## Upgrading from v2 to v3\n\nUpdate `composer.json` as described in \"Essentials\" above.\n\n\n## Upgrading from v1 to v2\nThere were significant database and code changes between v1 to v2.\n\nIf you're upgrading from v1 to v2, there's no built-in automatic migration/conversion of your data to the new structure. \nYou will need to carefully adapt your code and your data manually.\n\nTip: @fabricecw prepared [a gist which may make your data migration easier](https://gist.github.com/fabricecw/58ee93dd4f99e78724d8acbb851658a4). \n\nYou will also need to remove your old `laravel-permission.php` config file and publish the new one `permission.php`, and edit accordingly (setting up your custom settings again in the new file, where relevant).\n"
  },
  {
    "path": "ide.json",
    "content": "{\n    \"$schema\": \"https://laravel-ide.com/schema/laravel-ide-v2.json\",\n    \"blade\": {\n        \"directives\": [\n            {\n                \"name\": \"role\",\n                \"prefix\": \"<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {\",\n                \"suffix\": \"})): ?>\"\n            },\n            {\n                \"name\": \"elserole\",\n                \"prefix\": \"<?php elseif(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {\",\n                \"suffix\": \"})): ?>\"\n            },\n            {\n                \"name\": \"endrole\",\n                \"prefix\": \"\",\n                \"suffix\": \"\"\n            },\n            {\n                \"name\": \"hasrole\",\n                \"prefix\": \"<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {\",\n                \"suffix\": \"})): ?>\"\n            },\n            {\n                \"name\": \"endhasrole\",\n                \"prefix\": \"\",\n                \"suffix\": \"\"\n            },\n            {\n                \"name\": \"hasanyrole\",\n                \"prefix\": \"<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasAnyRole', {\",\n                \"suffix\": \"})): ?>\"\n            },\n            {\n                \"name\": \"endhasanyrole\",\n                \"prefix\": \"\",\n                \"suffix\": \"\"\n            },\n            {\n                \"name\": \"hasallroles\",\n                \"prefix\": \"<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasAllRoles', {\",\n                \"suffix\": \"})): ?>\"\n            },\n            {\n                \"name\": \"endhasallroles\",\n                \"prefix\": \"\",\n                \"suffix\": \"\"\n            },\n            {\n                \"name\": \"unlessrole\",\n                \"prefix\": \"<?php if(! \\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {\",\n                \"suffix\": \"})): ?>\"\n            },\n            {\n                \"name\": \"endunlessrole\",\n                \"prefix\": \"\",\n                \"suffix\": \"\"\n            },\n            {\n                \"name\": \"hasexactroles\",\n                \"prefix\": \"<?php if(\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper('hasExactRoles', {\",\n                \"suffix\": \"})): ?>\"\n            },\n            {\n                \"name\": \"endhasexactroles\",\n                \"prefix\": \"\",\n                \"suffix\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "phpstan-baseline.neon",
    "content": "parameters:\n\tignoreErrors:\n"
  },
  {
    "path": "phpstan.neon.dist",
    "content": "includes:\n    - ./vendor/larastan/larastan/extension.neon\n    - phpstan-baseline.neon\n\nparameters:\n    level: 5\n    paths:\n        - src\n        - config\n        - database/migrations/create_permission_tables.php.stub\n        - database/migrations/add_teams_fields.php.stub\n    tmpDir: build/phpstan\n    checkOctaneCompatibility: true\n\n    ignoreErrors:\n        - '#Unsafe usage of new static#'\n        # wildcard permissions:\n        - '#Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getWildcardClass#'\n        - '#Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getAllPermissions#'\n        # PermissionRegistrar accesses $roles on generic Model:\n        - '#Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$roles#'\n        # instanceof checks that are always false/true in specific trait contexts are intentional:\n        -\n            identifier: instanceof.alwaysFalse\n            path: src/Traits/HasPermissions.php\n        # unreachable code in trait contexts is intentional — traits are used in multiple model contexts:\n        - '#Unreachable statement - code above always terminates#'\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         bootstrap=\"vendor/autoload.php\"\n         colors=\"true\"\n         xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/10.4/phpunit.xsd\"\n>\n  <source>\n    <include>\n      <directory suffix=\".php\">src/</directory>\n    </include>\n  </source>\n  <testsuites>\n    <testsuite name=\"Permissions Test Suite\">\n      <directory>tests</directory>\n    </testsuite>\n  </testsuites>\n  <php>\n    <env name=\"CACHE_DRIVER\" value=\"array\"/>\n    <!-- APP_KEY required for Passport client -->\n    <env name=\"APP_KEY\" value=\"base64:W99w+5JYz8SVGf5sx17gmPR6uoNCtWiEVc+9qu8iGEg=\"/>\n    <ini name=\"memory_limit\" value=\"512M\"/>\n  </php>\n</phpunit>\n"
  },
  {
    "path": "pint.json",
    "content": "{\n    \"preset\": \"laravel\",\n    \"rules\": {\n        \"php_unit_method_casing\": false\n    }\n}\n"
  },
  {
    "path": "resources/boost/skills/laravel-permission-development/SKILL.md",
    "content": "---\nname: laravel-permission-development\ndescription: Build and work with Spatie Laravel Permission features, including roles, permissions, middleware, policies, teams, and Blade directives.\n---\n\n# Laravel Permission Development\n\n## When to use this skill\n\nUse this skill when working with authorization, roles, permissions, access control, middleware guards, or Blade permission directives using spatie/laravel-permission.\n\n## Core Concepts\n\n- **Users have Roles, Roles have Permissions, Apps check Permissions** (not Roles).\n- Direct permissions on users are an anti-pattern; assign permissions to roles instead.\n- Use `$user->can('permission-name')` for all authorization checks (supports Super Admin via Gate).\n- The `HasRoles` trait (which includes `HasPermissions`) is added to User models.\n\n## Setup\n\nAdd the `HasRoles` trait to your User model:\n\n```php\nuse Spatie\\Permission\\Traits\\HasRoles;\n\nclass User extends Authenticatable\n{\n    use HasRoles;\n}\n```\n\n## Creating Roles and Permissions\n\n```php\nuse Spatie\\Permission\\Models\\Role;\nuse Spatie\\Permission\\Models\\Permission;\n\n$role = Role::create(['name' => 'writer']);\n$permission = Permission::create(['name' => 'edit articles']);\n\n// findOrCreate is idempotent (safe for seeders)\n$role = Role::findOrCreate('writer', 'web');\n$permission = Permission::findOrCreate('edit articles', 'web');\n```\n\n## Assigning Roles and Permissions\n\n```php\n// Assign roles to users\n$user->assignRole('writer');\n$user->assignRole('writer', 'admin');\n$user->assignRole(['writer', 'admin']);\n$user->syncRoles(['writer', 'admin']); // replaces all\n$user->removeRole('writer');\n\n// Assign permissions to roles (preferred)\n$role->givePermissionTo('edit articles');\n$role->givePermissionTo(['edit articles', 'delete articles']);\n$role->syncPermissions(['edit articles', 'delete articles']);\n$role->revokePermissionTo('edit articles');\n\n// Reverse assignment\n$permission->assignRole('writer');\n$permission->syncRoles(['writer', 'editor']);\n$permission->removeRole('writer');\n```\n\n## Checking Roles and Permissions\n\n```php\n// Permission checks (preferred - supports Super Admin via Gate)\n$user->can('edit articles');\n$user->canAny(['edit articles', 'delete articles']);\n\n// Direct package methods (bypass Gate, no Super Admin support)\n$user->hasPermissionTo('edit articles');\n$user->hasAnyPermission(['edit articles', 'publish articles']);\n$user->hasAllPermissions(['edit articles', 'publish articles']);\n$user->hasDirectPermission('edit articles');\n\n// Role checks\n$user->hasRole('writer');\n$user->hasAnyRole(['writer', 'editor']);\n$user->hasAllRoles(['writer', 'editor']);\n$user->hasExactRoles(['writer', 'editor']);\n\n// Get assigned roles and permissions\n$user->getRoleNames();           // Collection of role name strings\n$user->getPermissionNames();     // Collection of permission name strings\n$user->getDirectPermissions();   // Direct permissions only\n$user->getPermissionsViaRoles(); // Inherited via roles\n$user->getAllPermissions();      // Both direct and inherited\n```\n\n## Query Scopes\n\n```php\n$users = User::role('writer')->get();\n$users = User::withoutRole('writer')->get();\n$users = User::permission('edit articles')->get();\n$users = User::withoutPermission('edit articles')->get();\n```\n\n## Middleware\n\nRegister middleware aliases in `bootstrap/app.php`:\n\n```php\n->withMiddleware(function (Middleware $middleware) {\n    $middleware->alias([\n        'role' => \\Spatie\\Permission\\Middleware\\RoleMiddleware::class,\n        'permission' => \\Spatie\\Permission\\Middleware\\PermissionMiddleware::class,\n        'role_or_permission' => \\Spatie\\Permission\\Middleware\\RoleOrPermissionMiddleware::class,\n    ]);\n})\n```\n\nUse in routes (pipe `|` for OR logic):\n\n```php\nRoute::middleware(['permission:edit articles'])->group(function () { ... });\nRoute::middleware(['role:manager|writer'])->group(function () { ... });\nRoute::middleware(['role_or_permission:manager|edit articles'])->group(function () { ... });\n\n// With specific guard\nRoute::middleware(['role:manager,api'])->group(function () { ... });\n```\n\nFor single permissions, Laravel's built-in `can` middleware also works:\n\n```php\nRoute::middleware(['can:edit articles'])->group(function () { ... });\n```\n\n## Blade Directives\n\nPrefer `@can` (permission-based) over `@role` (role-based):\n\n```blade\n@can('edit articles')\n    {{-- User can edit articles (supports Super Admin) --}}\n@endcan\n\n@canany(['edit articles', 'delete articles'])\n    {{-- User can do at least one --}}\n@endcanany\n\n@role('admin')\n    {{-- Only use for super-admin type checks --}}\n@endrole\n\n@hasanyrole('writer|admin')\n    {{-- Has writer or admin --}}\n@endhasanyrole\n```\n\n## Super Admin\n\nUse `Gate::before` in `AppServiceProvider::boot()`:\n\n```php\nuse Illuminate\\Support\\Facades\\Gate;\n\npublic function boot(): void\n{\n    Gate::before(function ($user, $ability) {\n        return $user->hasRole('Super Admin') ? true : null;\n    });\n}\n```\n\nThis makes `$user->can()` and `@can` always return true for Super Admins. Must return `null` (not `false`) to allow normal checks for other users.\n\n## Policies\n\nUse `$user->can()` inside policy methods to check permissions:\n\n```php\nclass PostPolicy\n{\n    public function update(User $user, Post $post): bool\n    {\n        if ($user->can('edit all posts')) {\n            return true;\n        }\n\n        return $user->can('edit own posts') && $user->id === $post->user_id;\n    }\n}\n```\n\n## Enums\n\n```php\nenum RolesEnum: string\n{\n    case WRITER = 'writer';\n    case EDITOR = 'editor';\n}\n\nenum PermissionsEnum: string\n{\n    case EDIT_POSTS = 'edit posts';\n    case DELETE_POSTS = 'delete posts';\n}\n\n// Creation requires ->value\nPermission::findOrCreate(PermissionsEnum::EDIT_POSTS->value, 'web');\n\n// Most methods accept enums directly\n$user->assignRole(RolesEnum::WRITER);\n$user->hasRole(RolesEnum::WRITER);\n$role->givePermissionTo(PermissionsEnum::EDIT_POSTS);\n$user->hasPermissionTo(PermissionsEnum::EDIT_POSTS);\n```\n\n## Seeding\n\nAlways flush the permission cache when seeding:\n\n```php\nclass RolesAndPermissionsSeeder extends Seeder\n{\n    public function run(): void\n    {\n        // Reset cache\n        app()[\\Spatie\\Permission\\PermissionRegistrar::class]->forgetCachedPermissions();\n\n        // Create permissions\n        Permission::findOrCreate('edit articles', 'web');\n        Permission::findOrCreate('delete articles', 'web');\n\n        // Create roles and assign permissions\n        Role::findOrCreate('writer', 'web')\n            ->givePermissionTo(['edit articles']);\n\n        Role::findOrCreate('admin', 'web')\n            ->givePermissionTo(Permission::all());\n    }\n}\n```\n\n## Teams (Multi-Tenancy)\n\nEnable in `config/permission.php` before running migrations:\n\n```php\n'teams' => true,\n```\n\nSet the active team in middleware:\n\n```php\nsetPermissionsTeamId($teamId);\n```\n\nWhen switching teams, unset cached relations:\n\n```php\n$user->unsetRelation('roles')->unsetRelation('permissions');\n```\n\n## Events\n\nEnable in `config/permission.php`:\n\n```php\n'events_enabled' => true,\n```\n\nAvailable events: `RoleAttachedEvent`, `RoleDetachedEvent`, `PermissionAttachedEvent`, `PermissionDetachedEvent` in the `Spatie\\Permission\\Events` namespace.\n\n## Performance\n\n- Permissions are cached automatically. The cache is flushed when roles/permissions change via package methods.\n- After direct DB operations, flush manually: `app()[\\Spatie\\Permission\\PermissionRegistrar::class]->forgetCachedPermissions()`\n- For bulk seeding, use `Permission::insert()` for speed, but flush the cache afterward.\n"
  },
  {
    "path": "src/Commands/AssignRoleCommand.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse Spatie\\Permission\\Contracts\\Role as RoleContract;\nuse Spatie\\Permission\\PermissionRegistrar;\n\nclass AssignRoleCommand extends Command\n{\n    protected $signature = 'permission:assign-role\n        {name : The name of the role}\n        {userId : The ID of the user to assign the role to}\n        {guard? : The name of the guard}\n        {userModelNamespace=App\\Models\\User : The fully qualified class name of the user model}\n        {--team-id=}';\n\n    protected $description = 'Assign a role to a user';\n\n    public function handle(PermissionRegistrar $permissionRegistrar): int\n    {\n        $roleName = $this->argument('name');\n        $userId = $this->argument('userId');\n        $guardName = $this->argument('guard');\n        $userModelClass = $this->argument('userModelNamespace');\n\n        if (! $permissionRegistrar->teams && $this->option('team-id')) {\n            $this->warn('Teams feature disabled, argument --team-id has no effect. Either enable it in permissions config file or remove --team-id parameter');\n\n            return self::SUCCESS;\n        }\n\n        // Validate that the model class exists and is instantiable\n        if (! class_exists($userModelClass)) {\n            $this->error(\"User model class [{$userModelClass}] does not exist.\");\n\n            return self::FAILURE;\n        }\n\n        $user = (new $userModelClass)::find($userId);\n\n        if (! $user) {\n            $this->error(\"User with ID {$userId} not found.\");\n\n            return self::FAILURE;\n        }\n\n        $teamIdAux = getPermissionsTeamId();\n        setPermissionsTeamId($this->option('team-id') ?: null);\n\n        /** @var \\Spatie\\Permission\\Contracts\\Role $roleClass */\n        $roleClass = app(RoleContract::class);\n\n        $role = $roleClass::findOrCreate($roleName, $guardName);\n\n        $user->assignRole($role);\n\n        setPermissionsTeamId($teamIdAux);\n\n        $this->info(\"Role `{$role->name}` assigned to user ID {$userId} successfully.\");\n\n        return self::SUCCESS;\n    }\n}\n"
  },
  {
    "path": "src/Commands/CacheResetCommand.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse Spatie\\Permission\\PermissionRegistrar;\n\nclass CacheResetCommand extends Command\n{\n    protected $signature = 'permission:cache-reset';\n\n    protected $description = 'Reset the permission cache';\n\n    public function handle(): int\n    {\n        $permissionRegistrar = app(PermissionRegistrar::class);\n        $cacheExists = $permissionRegistrar->getCacheRepository()->has($permissionRegistrar->cacheKey);\n\n        if ($permissionRegistrar->forgetCachedPermissions()) {\n            $this->info('Permission cache flushed.');\n        } elseif ($cacheExists) {\n            $this->error('Unable to flush cache.');\n        }\n\n        return self::SUCCESS;\n    }\n}\n"
  },
  {
    "path": "src/Commands/CreatePermissionCommand.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse Spatie\\Permission\\Contracts\\Permission as PermissionContract;\n\nclass CreatePermissionCommand extends Command\n{\n    protected $signature = 'permission:create-permission\n                {name : The name of the permission}\n                {guard? : The name of the guard}';\n\n    protected $description = 'Create a permission';\n\n    public function handle(): int\n    {\n        $permissionClass = app(PermissionContract::class);\n\n        $permission = $permissionClass::findOrCreate($this->argument('name'), $this->argument('guard'));\n\n        $this->info(\"Permission `{$permission->name}` \".($permission->wasRecentlyCreated ? 'created' : 'already exists'));\n\n        return self::SUCCESS;\n    }\n}\n"
  },
  {
    "path": "src/Commands/CreateRoleCommand.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Collection;\nuse Spatie\\Permission\\Contracts\\Permission as PermissionContract;\nuse Spatie\\Permission\\Contracts\\Role as RoleContract;\nuse Spatie\\Permission\\PermissionRegistrar;\n\nclass CreateRoleCommand extends Command\n{\n    protected $signature = 'permission:create-role\n        {name : The name of the role}\n        {guard? : The name of the guard}\n        {permissions? : A list of permissions to assign to the role, separated by | }\n        {--team-id=}';\n\n    protected $description = 'Create a role';\n\n    public function handle(PermissionRegistrar $permissionRegistrar): int\n    {\n        $roleClass = app(RoleContract::class);\n\n        $teamIdAux = getPermissionsTeamId();\n        setPermissionsTeamId($this->option('team-id') ?: null);\n\n        if (! $permissionRegistrar->teams && $this->option('team-id')) {\n            $this->warn('Teams feature disabled, argument --team-id has no effect. Either enable it in permissions config file or remove --team-id parameter');\n\n            return self::SUCCESS;\n        }\n\n        $role = $roleClass::findOrCreate($this->argument('name'), $this->argument('guard'));\n        setPermissionsTeamId($teamIdAux);\n\n        $teams_key = $permissionRegistrar->teamsKey;\n        if ($permissionRegistrar->teams && $this->option('team-id') && is_null($role->$teams_key)) {\n            $this->warn(\"Role `{$role->name}` already exists on the global team; argument --team-id has no effect\");\n        }\n\n        $role->givePermissionTo($this->makePermissions($this->argument('permissions')));\n\n        $this->info(\"Role `{$role->name}` \".($role->wasRecentlyCreated ? 'created' : 'updated'));\n\n        return self::SUCCESS;\n    }\n\n    protected function makePermissions(?string $string = null): ?Collection\n    {\n        if (empty($string)) {\n            return null;\n        }\n\n        $permissionClass = app(PermissionContract::class);\n\n        $permissions = explode('|', $string);\n\n        $models = [];\n\n        foreach ($permissions as $permission) {\n            $models[] = $permissionClass::findOrCreate(trim($permission), $this->argument('guard'));\n        }\n\n        return collect($models);\n    }\n}\n"
  },
  {
    "path": "src/Commands/ShowCommand.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Collection;\nuse Spatie\\Permission\\Contracts\\Permission as PermissionContract;\nuse Spatie\\Permission\\Contracts\\Role as RoleContract;\nuse Symfony\\Component\\Console\\Helper\\TableCell;\n\nclass ShowCommand extends Command\n{\n    protected $signature = 'permission:show\n            {guard? : The name of the guard}\n            {style? : The display style (default|borderless|compact|box)}';\n\n    protected $description = 'Show a table of roles and permissions per guard';\n\n    public function handle(): int\n    {\n        $permissionClass = app(PermissionContract::class);\n        $roleClass = app(RoleContract::class);\n        $teamsEnabled = config('permission.teams');\n        $team_key = config('permission.column_names.team_foreign_key');\n\n        $style = $this->argument('style') ?? 'default';\n        $guard = $this->argument('guard');\n\n        if ($guard) {\n            $guards = Collection::make([$guard]);\n        } else {\n            $guards = $permissionClass::pluck('guard_name')->merge($roleClass::pluck('guard_name'))->unique();\n        }\n\n        foreach ($guards as $guard) {\n            $this->info(\"Guard: $guard\");\n\n            $roles = $roleClass::whereGuardName($guard)\n                ->with('permissions')\n                ->when($teamsEnabled, fn ($q) => $q->orderBy($team_key))\n                ->orderBy('name')->get()->mapWithKeys(fn ($role) => [\n                    $role->name.'_'.($teamsEnabled ? ($role->$team_key ?: '') : '') => [\n                        'permissions' => $role->permissions->pluck($permissionClass->getKeyName()),\n                        $team_key => $teamsEnabled ? $role->$team_key : null,\n                    ],\n                ]);\n\n            $permissions = $permissionClass::whereGuardName($guard)->orderBy('name')->pluck('name', $permissionClass->getKeyName());\n\n            $body = $permissions->map(fn ($permission, $id) => $roles->map(\n                fn (array $role_data) => $role_data['permissions']->contains($id) ? ' ✔' : ' ·'\n            )->prepend($permission)\n            );\n\n            if ($teamsEnabled) {\n                $teams = $roles->groupBy($team_key)->values()->map(\n                    fn ($group, $id) => new TableCell('Team ID: '.($id ?: 'NULL'), ['colspan' => $group->count()])\n                );\n            }\n\n            $this->table(\n                array_merge(\n                    isset($teams) ? $teams->prepend(new TableCell(''))->toArray() : [],\n                    $roles->keys()->map(function ($val) {\n                        $name = explode('_', $val);\n                        array_pop($name);\n\n                        return implode('_', $name);\n                    })\n                        ->prepend(new TableCell(''))->toArray(),\n                ),\n                $body->toArray(),\n                $style\n            );\n        }\n\n        return self::SUCCESS;\n    }\n}\n"
  },
  {
    "path": "src/Commands/UpgradeForTeamsCommand.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Config;\n\nclass UpgradeForTeamsCommand extends Command\n{\n    protected $signature = 'permission:setup-teams';\n\n    protected $description = 'Setup the teams feature by generating the associated migration.';\n\n    protected string $migrationSuffix = 'add_teams_fields.php';\n\n    public function handle(): int\n    {\n        if (! Config::get('permission.teams')) {\n            $this->error('Teams feature is disabled in your permission.php file.');\n            $this->warn('Please enable the teams setting in your configuration.');\n\n            return self::FAILURE;\n        }\n\n        $this->line('');\n        $this->info('The teams feature setup is going to add a migration and a model');\n\n        $existingMigrations = $this->alreadyExistingMigrations();\n\n        if ($existingMigrations) {\n            $this->line('');\n\n            $this->warn($this->getExistingMigrationsWarning($existingMigrations));\n        }\n\n        $this->line('');\n\n        if (! $this->confirm('Proceed with the migration creation?', true)) {\n            return self::SUCCESS;\n        }\n\n        $this->line('');\n\n        $this->line('Creating migration');\n\n        if ($this->createMigration()) {\n            $this->info('Migration created successfully.');\n        } else {\n            $this->error(\n                \"Couldn't create migration.\\n\".\n                'Check the write permissions within the database/migrations directory.'\n            );\n        }\n\n        $this->line('');\n\n        return self::SUCCESS;\n    }\n\n    protected function createMigration(): bool\n    {\n        try {\n            $migrationStub = __DIR__.\"/../../database/migrations/{$this->migrationSuffix}.stub\";\n            copy($migrationStub, $this->getMigrationPath());\n\n            return true;\n        } catch (\\Throwable $e) {\n            $this->error($e->getMessage());\n\n            return false;\n        }\n    }\n\n    protected function getExistingMigrationsWarning(array $existingMigrations): string\n    {\n        if (count($existingMigrations) > 1) {\n            $base = \"Setup teams migrations already exist.\\nFollowing files were found: \";\n        } else {\n            $base = \"Setup teams migration already exists.\\nFollowing file was found: \";\n        }\n\n        return $base.array_reduce($existingMigrations, fn ($carry, $fileName) => $carry.\"\\n - \".$fileName);\n    }\n\n    protected function alreadyExistingMigrations(): array\n    {\n        $matchingFiles = glob($this->getMigrationPath('*'));\n\n        return array_map(fn ($path) => basename($path), $matchingFiles);\n    }\n\n    protected function getMigrationPath(?string $date = null): string\n    {\n        $date = $date ?: now()->format('Y_m_d_His');\n\n        return database_path(\"migrations/{$date}_{$this->migrationSuffix}\");\n    }\n}\n"
  },
  {
    "path": "src/Contracts/Permission.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Contracts;\n\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\n\n/**\n * @property int|string $id\n * @property string $name\n * @property string|null $guard_name\n *\n * @mixin \\Spatie\\Permission\\Models\\Permission\n *\n * @phpstan-require-extends \\Spatie\\Permission\\Models\\Permission\n */\ninterface Permission\n{\n    /**\n     * A permission can be applied to roles.\n     */\n    public function roles(): BelongsToMany;\n\n    /**\n     * Find a permission by its name.\n     *\n     *\n     * @throws \\Spatie\\Permission\\Exceptions\\PermissionDoesNotExist\n     */\n    public static function findByName(string $name, ?string $guardName): self;\n\n    /**\n     * Find a permission by its id.\n     *\n     *\n     * @throws \\Spatie\\Permission\\Exceptions\\PermissionDoesNotExist\n     */\n    public static function findById(int|string $id, ?string $guardName): self;\n\n    /**\n     * Find or Create a permission by its name and guard name.\n     */\n    public static function findOrCreate(string $name, ?string $guardName): self;\n}\n"
  },
  {
    "path": "src/Contracts/PermissionsTeamResolver.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Contracts;\n\nuse Illuminate\\Database\\Eloquent\\Model;\n\ninterface PermissionsTeamResolver\n{\n    public function getPermissionsTeamId(): int|string|null;\n\n    public function setPermissionsTeamId(int|string|Model|null $id): void;\n}\n"
  },
  {
    "path": "src/Contracts/Role.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Contracts;\n\nuse BackedEnum;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\n\n/**\n * @property int|string $id\n * @property string $name\n * @property string|null $guard_name\n *\n * @mixin \\Spatie\\Permission\\Models\\Role\n *\n * @phpstan-require-extends \\Spatie\\Permission\\Models\\Role\n */\ninterface Role\n{\n    /**\n     * A role may be given various permissions.\n     */\n    public function permissions(): BelongsToMany;\n\n    /**\n     * Find a role by its name and guard name.\n     *\n     *\n     * @throws \\Spatie\\Permission\\Exceptions\\RoleDoesNotExist\n     */\n    public static function findByName(string $name, ?string $guardName): self;\n\n    /**\n     * Find a role by its id and guard name.\n     *\n     *\n     * @throws \\Spatie\\Permission\\Exceptions\\RoleDoesNotExist\n     */\n    public static function findById(int|string $id, ?string $guardName): self;\n\n    /**\n     * Find or create a role by its name and guard name.\n     */\n    public static function findOrCreate(string $name, ?string $guardName): self;\n\n    /**\n     * Determine if the user may perform the given permission.\n     */\n    public function hasPermissionTo(string|int|Permission|BackedEnum $permission, ?string $guardName = null): bool;\n}\n"
  },
  {
    "path": "src/Contracts/Wildcard.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Contracts;\n\ninterface Wildcard\n{\n    public function getIndex(): array;\n\n    public function implies(string $permission, string $guardName, array $index): bool;\n}\n"
  },
  {
    "path": "src/DefaultTeamResolver.php",
    "content": "<?php\n\nnamespace Spatie\\Permission;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Spatie\\Permission\\Contracts\\PermissionsTeamResolver;\n\nclass DefaultTeamResolver implements PermissionsTeamResolver\n{\n    protected int|string|null $teamId = null;\n\n    public function setPermissionsTeamId(int|string|Model|null $id): void\n    {\n        if ($id instanceof Model) {\n            $id = $id->getKey();\n        }\n        $this->teamId = $id;\n    }\n\n    public function getPermissionsTeamId(): int|string|null\n    {\n        return $this->teamId;\n    }\n}\n"
  },
  {
    "path": "src/Events/PermissionAttachedEvent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Spatie\\Permission\\Events;\n\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Foundation\\Bus\\Dispatchable;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Collection;\nuse Spatie\\Permission\\Contracts\\Permission;\n\nclass PermissionAttachedEvent\n{\n    use Dispatchable;\n    use InteractsWithSockets;\n    use SerializesModels;\n\n    /**\n     * Internally the HasPermissions trait passes an array of permission ids (eg: int's or uuid's)\n     * Theoretically one could register the event to other places and pass an Eloquent record.\n     * So a Listener should inspect the type of $permissionsOrIds received before using.\n     *\n     * @param  array|int[]|string[]|Permission|Permission[]|Collection  $permissionsOrIds\n     */\n    public function __construct(public Model $model, public mixed $permissionsOrIds) {}\n}\n"
  },
  {
    "path": "src/Events/PermissionDetachedEvent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Spatie\\Permission\\Events;\n\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Foundation\\Bus\\Dispatchable;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Collection;\nuse Spatie\\Permission\\Contracts\\Permission;\n\nclass PermissionDetachedEvent\n{\n    use Dispatchable;\n    use InteractsWithSockets;\n    use SerializesModels;\n\n    /**\n     * Internally the HasPermissions trait passes $permissionsOrIds as an Eloquent record.\n     * Theoretically one could register the event to other places and pass an array etc.\n     * So a Listener should inspect the type of $permissionsOrIds received before using.\n     *\n     * @param  array|int[]|string[]|Permission|Permission[]|Collection  $permissionsOrIds\n     */\n    public function __construct(public Model $model, public mixed $permissionsOrIds) {}\n}\n"
  },
  {
    "path": "src/Events/RoleAttachedEvent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Spatie\\Permission\\Events;\n\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Foundation\\Bus\\Dispatchable;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Collection;\nuse Spatie\\Permission\\Contracts\\Role;\n\nclass RoleAttachedEvent\n{\n    use Dispatchable;\n    use InteractsWithSockets;\n    use SerializesModels;\n\n    /**\n     * Internally the HasRoles trait passes an array of role ids (eg: int's or uuid's)\n     * Theoretically one could register the event to other places passing other types\n     * So a Listener should inspect the type of $rolesOrIds received before using.\n     *\n     * @param  array|int[]|string[]|Role|Role[]|Collection  $rolesOrIds\n     */\n    public function __construct(public Model $model, public mixed $rolesOrIds) {}\n}\n"
  },
  {
    "path": "src/Events/RoleDetachedEvent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Spatie\\Permission\\Events;\n\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Foundation\\Bus\\Dispatchable;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Collection;\nuse Spatie\\Permission\\Contracts\\Role;\n\nclass RoleDetachedEvent\n{\n    use Dispatchable;\n    use InteractsWithSockets;\n    use SerializesModels;\n\n    /**\n     * Internally the HasRoles trait passes an array of role ids (eg: int's or uuid's)\n     * Theoretically one could register the event to other places passing other types\n     * So a Listener should inspect the type of $rolesOrIds received before using.\n     *\n     * @param  array|int[]|string[]|Role|Role[]|Collection  $rolesOrIds\n     */\n    public function __construct(public Model $model, public mixed $rolesOrIds) {}\n}\n"
  },
  {
    "path": "src/Exceptions/GuardDoesNotMatch.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Exceptions;\n\nuse Illuminate\\Support\\Collection;\nuse InvalidArgumentException;\n\nclass GuardDoesNotMatch extends InvalidArgumentException\n{\n    public static function create(string $givenGuard, Collection $expectedGuards): static\n    {\n        return new static(__('The given role or permission should use guard `:expected` instead of `:given`.', [\n            'expected' => $expectedGuards->implode(', '),\n            'given' => $givenGuard,\n        ]));\n    }\n}\n"
  },
  {
    "path": "src/Exceptions/PermissionAlreadyExists.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Exceptions;\n\nuse InvalidArgumentException;\n\nclass PermissionAlreadyExists extends InvalidArgumentException\n{\n    public static function create(string $permissionName, string $guardName): static\n    {\n        return new static(__('A `:permission` permission already exists for guard `:guard`.', [\n            'permission' => $permissionName,\n            'guard' => $guardName,\n        ]));\n    }\n}\n"
  },
  {
    "path": "src/Exceptions/PermissionDoesNotExist.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Exceptions;\n\nuse InvalidArgumentException;\n\nclass PermissionDoesNotExist extends InvalidArgumentException\n{\n    public static function create(string $permissionName, ?string $guardName): static\n    {\n        return new static(__('There is no permission named `:permission` for guard `:guard`.', [\n            'permission' => $permissionName,\n            'guard' => $guardName,\n        ]));\n    }\n\n    public static function withId(int|string $permissionId, ?string $guardName): static\n    {\n        return new static(__('There is no [permission] with ID `:id` for guard `:guard`.', [\n            'id' => $permissionId,\n            'guard' => $guardName,\n        ]));\n    }\n}\n"
  },
  {
    "path": "src/Exceptions/RoleAlreadyExists.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Exceptions;\n\nuse InvalidArgumentException;\n\nclass RoleAlreadyExists extends InvalidArgumentException\n{\n    public static function create(string $roleName, string $guardName): static\n    {\n        return new static(__('A role `:role` already exists for guard `:guard`.', [\n            'role' => $roleName,\n            'guard' => $guardName,\n        ]));\n    }\n}\n"
  },
  {
    "path": "src/Exceptions/RoleDoesNotExist.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Exceptions;\n\nuse InvalidArgumentException;\n\nclass RoleDoesNotExist extends InvalidArgumentException\n{\n    public static function named(string $roleName, ?string $guardName): static\n    {\n        return new static(__('There is no role named `:role` for guard `:guard`.', [\n            'role' => $roleName,\n            'guard' => $guardName,\n        ]));\n    }\n\n    public static function withId(int|string $roleId, ?string $guardName): static\n    {\n        return new static(__('There is no role with ID `:id` for guard `:guard`.', [\n            'id' => $roleId,\n            'guard' => $guardName,\n        ]));\n    }\n}\n"
  },
  {
    "path": "src/Exceptions/UnauthorizedException.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Exceptions;\n\nuse Illuminate\\Contracts\\Auth\\Access\\Authorizable;\nuse Symfony\\Component\\HttpKernel\\Exception\\HttpException;\n\nclass UnauthorizedException extends HttpException\n{\n    private array $requiredRoles = [];\n\n    private array $requiredPermissions = [];\n\n    public static function forRoles(array $roles): static\n    {\n        $message = __('User does not have the right roles.');\n\n        if (config('permission.display_role_in_exception')) {\n            $message .= ' '.__('Necessary roles are :roles', ['roles' => implode(', ', $roles)]);\n        }\n\n        $exception = new static(403, $message, null, []);\n        $exception->requiredRoles = $roles;\n\n        return $exception;\n    }\n\n    public static function forPermissions(array $permissions): static\n    {\n        $message = __('User does not have the right permissions.');\n\n        if (config('permission.display_permission_in_exception')) {\n            $message .= ' '.__('Necessary permissions are :permissions', ['permissions' => implode(', ', $permissions)]);\n        }\n\n        $exception = new static(403, $message, null, []);\n        $exception->requiredPermissions = $permissions;\n\n        return $exception;\n    }\n\n    public static function forRolesOrPermissions(array $rolesOrPermissions): static\n    {\n        $message = __('User does not have any of the necessary access rights.');\n\n        if (config('permission.display_permission_in_exception') && config('permission.display_role_in_exception')) {\n            $message .= ' '.__('Necessary roles or permissions are :values', ['values' => implode(', ', $rolesOrPermissions)]);\n        }\n\n        $exception = new static(403, $message, null, []);\n        $exception->requiredPermissions = $rolesOrPermissions;\n\n        return $exception;\n    }\n\n    public static function missingTraitHasRoles(Authorizable $user): static\n    {\n        return new static(403, __('Authorizable class `:class` must use Spatie\\\\Permission\\\\Traits\\\\HasRoles trait.', [\n            'class' => $user::class,\n        ]), null, []);\n    }\n\n    public static function notLoggedIn(): static\n    {\n        return new static(403, __('User is not logged in.'), null, []);\n    }\n\n    public function getRequiredRoles(): array\n    {\n        return $this->requiredRoles;\n    }\n\n    public function getRequiredPermissions(): array\n    {\n        return $this->requiredPermissions;\n    }\n}\n"
  },
  {
    "path": "src/Exceptions/WildcardPermissionInvalidArgument.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Exceptions;\n\nuse InvalidArgumentException;\n\nclass WildcardPermissionInvalidArgument extends InvalidArgumentException\n{\n    public static function create(): static\n    {\n        return new static(__('Wildcard permission must be string, permission id or permission instance'));\n    }\n}\n"
  },
  {
    "path": "src/Exceptions/WildcardPermissionNotImplementsContract.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Exceptions;\n\nuse InvalidArgumentException;\n\nclass WildcardPermissionNotImplementsContract extends InvalidArgumentException\n{\n    public static function create(): static\n    {\n        return new static(__('Wildcard permission class must implement Spatie\\\\Permission\\\\Contracts\\\\Wildcard contract'));\n    }\n}\n"
  },
  {
    "path": "src/Exceptions/WildcardPermissionNotProperlyFormatted.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Exceptions;\n\nuse InvalidArgumentException;\n\nclass WildcardPermissionNotProperlyFormatted extends InvalidArgumentException\n{\n    public static function create(string $permission): static\n    {\n        return new static(__('Wildcard permission `:permission` is not properly formatted.', [\n            'permission' => $permission,\n        ]));\n    }\n}\n"
  },
  {
    "path": "src/Guard.php",
    "content": "<?php\n\nnamespace Spatie\\Permission;\n\nuse Illuminate\\Contracts\\Auth\\Access\\Authorizable;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Auth;\nuse ReflectionClass;\n\nclass Guard\n{\n    /**\n     * Return a collection of guard names suitable for the $model,\n     * as indicated by the presence of a $guard_name property or a guardName() method on the model.\n     *\n     * @param  string|Model  $model  model class object or name\n     */\n    public static function getNames(string|Model $model): Collection\n    {\n        $class = is_object($model) ? $model::class : $model;\n\n        if (is_object($model)) {\n            if (method_exists($model, 'guardName')) {\n                $guardName = $model->guardName();\n            } else {\n                $guardName = $model->getAttributeValue('guard_name');\n            }\n        }\n\n        if (! isset($guardName)) {\n            $guardName = (new ReflectionClass($class))->getDefaultProperties()['guard_name'] ?? null;\n        }\n\n        if ($guardName) {\n            return collect($guardName);\n        }\n\n        return self::getConfigAuthGuards($class);\n    }\n\n    /**\n     * Get the model class associated with a given provider.\n     */\n    protected static function getProviderModel(string $provider): ?string\n    {\n        // Get the provider configuration\n        $providerConfig = config(\"auth.providers.{$provider}\");\n\n        // Handle LDAP provider or standard Eloquent provider\n        if (isset($providerConfig['driver']) && $providerConfig['driver'] === 'ldap') {\n            return $providerConfig['database']['model'] ?? null;\n        }\n\n        return $providerConfig['model'] ?? null;\n    }\n\n    /**\n     * Get list of relevant guards for the $class model based on config(auth) settings.\n     *\n     * Lookup flow:\n     * - get names of models for guards defined in auth.guards where a provider is set\n     * - filter for provider models matching the model $class being checked\n     * - keys() gives just the names of the matched guards\n     * - return collection of guard names\n     */\n    protected static function getConfigAuthGuards(string $class): Collection\n    {\n        return collect(config('auth.guards'))\n            ->map(function ($guard) {\n                if (! isset($guard['provider'])) {\n                    return null;\n                }\n\n                return static::getProviderModel($guard['provider']);\n            })\n            ->filter(fn ($model) => $class === $model)\n            ->keys();\n    }\n\n    /**\n     * Get the model associated with a given guard name.\n     */\n    public static function getModelForGuard(string $guard): ?string\n    {\n        // Get the provider configuration for the given guard\n        $provider = config(\"auth.guards.{$guard}.provider\");\n\n        if (! $provider) {\n            return null;\n        }\n\n        return static::getProviderModel($provider);\n    }\n\n    /**\n     * Lookup a guard name relevant for the $class model and the current user.\n     *\n     * @param  string|Model  $class  model class object or name\n     */\n    public static function getDefaultName(string|Model $class): string\n    {\n        $default = config('auth.defaults.guard');\n\n        $possible_guards = static::getNames($class);\n\n        // return current-detected auth.defaults.guard if it matches one of those that have been checked\n        if ($possible_guards->contains($default)) {\n            return $default;\n        }\n\n        return $possible_guards->first() ?: $default;\n    }\n\n    /**\n     * Lookup a passport guard\n     */\n    public static function getPassportClient(?string $guard): ?Authorizable\n    {\n        $guards = collect(config('auth.guards'))->where('driver', 'passport');\n\n        if (! $guards->count()) {\n            return null;\n        }\n\n        $authGuard = Auth::guard($guards->keys()[0]);\n\n        if (! method_exists($authGuard, 'client')) {\n            return null;\n        }\n\n        $client = $authGuard->client();\n\n        if (! $guard || ! $client) {\n            return $client;\n        }\n\n        if (self::getNames($client)->contains($guard)) {\n            return $client;\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/Middleware/PermissionMiddleware.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Middleware;\n\nuse BackedEnum;\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Spatie\\Permission\\Exceptions\\UnauthorizedException;\nuse Spatie\\Permission\\Guard;\n\nuse function Illuminate\\Support\\enum_value;\n\nclass PermissionMiddleware\n{\n    public function handle(Request $request, Closure $next, $permission, ?string $guard = null)\n    {\n        $authGuard = Auth::guard($guard);\n\n        $user = $authGuard->user();\n\n        // For machine-to-machine Passport clients\n        if (! $user && $request->bearerToken() && config('permission.use_passport_client_credentials')) {\n            $user = Guard::getPassportClient($guard);\n        }\n\n        if (! $user) {\n            throw UnauthorizedException::notLoggedIn();\n        }\n\n        if (! method_exists($user, 'hasAnyPermission')) {\n            throw UnauthorizedException::missingTraitHasRoles($user);\n        }\n\n        $permissions = explode('|', self::parsePermissionsToString($permission));\n\n        if (! $user->canAny($permissions)) {\n            throw UnauthorizedException::forPermissions($permissions);\n        }\n\n        return $next($request);\n    }\n\n    /**\n     * Specify the permission and guard for the middleware.\n     */\n    public static function using(array|string|BackedEnum $permission, ?string $guard = null): string\n    {\n        $permissionString = self::parsePermissionsToString(enum_value($permission));\n\n        $args = is_null($guard) ? $permissionString : \"$permissionString,$guard\";\n\n        return static::class.':'.$args;\n    }\n\n    protected static function parsePermissionsToString(array|string|BackedEnum $permission): string\n    {\n        $permission = enum_value($permission);\n\n        if (is_array($permission)) {\n            return implode('|', array_map(fn ($r) => enum_value($r), $permission));\n        }\n\n        return (string) $permission;\n    }\n}\n"
  },
  {
    "path": "src/Middleware/RoleMiddleware.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Middleware;\n\nuse BackedEnum;\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Spatie\\Permission\\Exceptions\\UnauthorizedException;\nuse Spatie\\Permission\\Guard;\n\nuse function Illuminate\\Support\\enum_value;\n\nclass RoleMiddleware\n{\n    public function handle(Request $request, Closure $next, $role, ?string $guard = null)\n    {\n        $authGuard = Auth::guard($guard);\n\n        $user = $authGuard->user();\n\n        // For machine-to-machine Passport clients\n        if (! $user && $request->bearerToken() && config('permission.use_passport_client_credentials')) {\n            $user = Guard::getPassportClient($guard);\n        }\n\n        if (! $user) {\n            throw UnauthorizedException::notLoggedIn();\n        }\n\n        if (! method_exists($user, 'hasAnyRole')) {\n            throw UnauthorizedException::missingTraitHasRoles($user);\n        }\n\n        $roles = explode('|', self::parseRolesToString($role));\n\n        if (! $user->hasAnyRole($roles)) {\n            throw UnauthorizedException::forRoles($roles);\n        }\n\n        return $next($request);\n    }\n\n    /**\n     * Specify the role and guard for the middleware.\n     */\n    public static function using(array|string|BackedEnum $role, ?string $guard = null): string\n    {\n        $roleString = self::parseRolesToString($role);\n\n        $args = is_null($guard) ? $roleString : \"$roleString,$guard\";\n\n        return static::class.':'.$args;\n    }\n\n    protected static function parseRolesToString(array|string|BackedEnum $role): string\n    {\n        $role = enum_value($role);\n\n        if (is_array($role)) {\n            return implode('|', array_map(fn ($r) => enum_value($r), $role));\n        }\n\n        return (string) $role;\n    }\n}\n"
  },
  {
    "path": "src/Middleware/RoleOrPermissionMiddleware.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Middleware;\n\nuse BackedEnum;\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Spatie\\Permission\\Exceptions\\UnauthorizedException;\nuse Spatie\\Permission\\Guard;\n\nuse function Illuminate\\Support\\enum_value;\n\nclass RoleOrPermissionMiddleware\n{\n    public function handle(Request $request, Closure $next, $roleOrPermission, ?string $guard = null)\n    {\n        $authGuard = Auth::guard($guard);\n\n        $user = $authGuard->user();\n\n        // For machine-to-machine Passport clients\n        if (! $user && $request->bearerToken() && config('permission.use_passport_client_credentials')) {\n            $user = Guard::getPassportClient($guard);\n        }\n\n        if (! $user) {\n            throw UnauthorizedException::notLoggedIn();\n        }\n\n        if (! method_exists($user, 'hasAnyRole') || ! method_exists($user, 'hasAnyPermission')) {\n            throw UnauthorizedException::missingTraitHasRoles($user);\n        }\n\n        $rolesOrPermissions = explode('|', self::parseRoleOrPermissionToString($roleOrPermission));\n\n        if (! $user->canAny($rolesOrPermissions) && ! $user->hasAnyRole($rolesOrPermissions)) {\n            throw UnauthorizedException::forRolesOrPermissions($rolesOrPermissions);\n        }\n\n        return $next($request);\n    }\n\n    /**\n     * Specify the role or permission and guard for the middleware.\n     */\n    public static function using(array|string|BackedEnum $roleOrPermission, ?string $guard = null): string\n    {\n        $roleOrPermissionString = self::parseRoleOrPermissionToString($roleOrPermission);\n        $args = is_null($guard) ? $roleOrPermissionString : \"$roleOrPermissionString,$guard\";\n\n        return static::class.':'.$args;\n    }\n\n    protected static function parseRoleOrPermissionToString(array|string|BackedEnum $roleOrPermission): string\n    {\n        $roleOrPermission = enum_value($roleOrPermission);\n\n        if (is_array($roleOrPermission)) {\n            return implode('|', array_map(fn ($r) => enum_value($r), $roleOrPermission));\n        }\n\n        return (string) $roleOrPermission;\n    }\n}\n"
  },
  {
    "path": "src/Models/Permission.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Collection;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Spatie\\Permission\\Contracts\\Permission as PermissionContract;\nuse Spatie\\Permission\\Exceptions\\PermissionAlreadyExists;\nuse Spatie\\Permission\\Exceptions\\PermissionDoesNotExist;\nuse Spatie\\Permission\\Guard;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\Traits\\HasRoles;\nuse Spatie\\Permission\\Traits\\RefreshesPermissionCache;\n\n/**\n * @property int|string $id\n * @property string $name\n * @property string $guard_name\n * @property ?\\Illuminate\\Support\\Carbon $created_at\n * @property ?\\Illuminate\\Support\\Carbon $updated_at\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Spatie\\Permission\\Models\\Role> $roles\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Illuminate\\Database\\Eloquent\\Model> $users\n */\nclass Permission extends Model implements PermissionContract\n{\n    use HasRoles;\n    use RefreshesPermissionCache;\n\n    protected $guarded = [];\n\n    public function __construct(array $attributes = [])\n    {\n        $attributes['guard_name'] ??= Guard::getDefaultName(static::class);\n\n        parent::__construct($attributes);\n\n        $this->guarded[] = $this->primaryKey;\n        $this->table = config('permission.table_names.permissions') ?: parent::getTable();\n    }\n\n    /**\n     * @return PermissionContract|Permission\n     *\n     * @throws PermissionAlreadyExists\n     */\n    public static function create(array $attributes = [])\n    {\n        $attributes['guard_name'] ??= Guard::getDefaultName(static::class);\n\n        $permission = static::getPermission(['name' => $attributes['name'], 'guard_name' => $attributes['guard_name']]);\n\n        if ($permission) {\n            throw PermissionAlreadyExists::create($attributes['name'], $attributes['guard_name']);\n        }\n\n        return static::query()->create($attributes);\n    }\n\n    /**\n     * A permission can be applied to roles.\n     */\n    public function roles(): BelongsToMany\n    {\n        $registrar = app(PermissionRegistrar::class);\n\n        return $this->belongsToMany(\n            config('permission.models.role'),\n            config('permission.table_names.role_has_permissions'),\n            $registrar->pivotPermission,\n            $registrar->pivotRole\n        );\n    }\n\n    /**\n     * A permission belongs to some users of the model associated with its guard.\n     */\n    public function users(): BelongsToMany\n    {\n        return $this->morphedByMany(\n            getModelForGuard($this->attributes['guard_name'] ?? config('auth.defaults.guard')),\n            'model',\n            config('permission.table_names.model_has_permissions'),\n            app(PermissionRegistrar::class)->pivotPermission,\n            config('permission.column_names.model_morph_key')\n        );\n    }\n\n    /**\n     * Find a permission by its name (and optionally guardName).\n     *\n     * @return PermissionContract|Permission\n     *\n     * @throws PermissionDoesNotExist\n     */\n    public static function findByName(string $name, ?string $guardName = null): PermissionContract\n    {\n        $guardName ??= Guard::getDefaultName(static::class);\n        $permission = static::getPermission(['name' => $name, 'guard_name' => $guardName]);\n        if (! $permission) {\n            throw PermissionDoesNotExist::create($name, $guardName);\n        }\n\n        return $permission;\n    }\n\n    /**\n     * Find a permission by its id (and optionally guardName).\n     *\n     * @return PermissionContract|Permission\n     *\n     * @throws PermissionDoesNotExist\n     */\n    public static function findById(int|string $id, ?string $guardName = null): PermissionContract\n    {\n        $guardName ??= Guard::getDefaultName(static::class);\n        $permission = static::getPermission([(new static)->getKeyName() => $id, 'guard_name' => $guardName]);\n\n        if (! $permission) {\n            throw PermissionDoesNotExist::withId($id, $guardName);\n        }\n\n        return $permission;\n    }\n\n    /**\n     * Find or create permission by its name (and optionally guardName).\n     *\n     * @return PermissionContract|Permission\n     */\n    public static function findOrCreate(string $name, ?string $guardName = null): PermissionContract\n    {\n        $guardName ??= Guard::getDefaultName(static::class);\n        $permission = static::getPermission(['name' => $name, 'guard_name' => $guardName]);\n\n        if (! $permission) {\n            return static::query()->create(['name' => $name, 'guard_name' => $guardName]);\n        }\n\n        return $permission;\n    }\n\n    /**\n     * Get the current cached permissions.\n     */\n    protected static function getPermissions(array $params = [], bool $onlyOne = false): Collection\n    {\n        return app(PermissionRegistrar::class)\n            ->setPermissionClass(static::class)\n            ->getPermissions($params, $onlyOne);\n    }\n\n    /**\n     * Get the current cached first permission.\n     *\n     * @return PermissionContract|Permission|null\n     */\n    protected static function getPermission(array $params = []): ?PermissionContract\n    {\n        /** @var PermissionContract|null */\n        return static::getPermissions($params, true)->first();\n    }\n}\n"
  },
  {
    "path": "src/Models/Role.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Models;\n\nuse BackedEnum;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Spatie\\Permission\\Contracts\\Role as RoleContract;\nuse Spatie\\Permission\\Exceptions\\GuardDoesNotMatch;\nuse Spatie\\Permission\\Exceptions\\PermissionDoesNotExist;\nuse Spatie\\Permission\\Exceptions\\RoleAlreadyExists;\nuse Spatie\\Permission\\Exceptions\\RoleDoesNotExist;\nuse Spatie\\Permission\\Guard;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\Traits\\HasPermissions;\nuse Spatie\\Permission\\Traits\\RefreshesPermissionCache;\n\n/**\n * @property int|string $id\n * @property string $name\n * @property string $guard_name\n * @property ?\\Illuminate\\Support\\Carbon $created_at\n * @property ?\\Illuminate\\Support\\Carbon $updated_at\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Spatie\\Permission\\Models\\Permission> $permissions\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Illuminate\\Database\\Eloquent\\Model> $users\n */\nclass Role extends Model implements RoleContract\n{\n    use HasPermissions;\n    use RefreshesPermissionCache;\n\n    protected $guarded = [];\n\n    public function __construct(array $attributes = [])\n    {\n        $attributes['guard_name'] ??= Guard::getDefaultName(static::class);\n\n        parent::__construct($attributes);\n\n        $this->guarded[] = $this->primaryKey;\n        $this->table = config('permission.table_names.roles') ?: parent::getTable();\n    }\n\n    /**\n     * @return RoleContract|Role\n     *\n     * @throws RoleAlreadyExists\n     */\n    public static function create(array $attributes = [])\n    {\n        $attributes['guard_name'] ??= Guard::getDefaultName(static::class);\n\n        $params = ['name' => $attributes['name'], 'guard_name' => $attributes['guard_name']];\n\n        $registrar = app(PermissionRegistrar::class);\n\n        if ($registrar->teams) {\n            $teamsKey = $registrar->teamsKey;\n\n            if (array_key_exists($teamsKey, $attributes)) {\n                $params[$teamsKey] = $attributes[$teamsKey];\n            } else {\n                $attributes[$teamsKey] = getPermissionsTeamId();\n            }\n        }\n\n        if (static::findByParam($params)) {\n            throw RoleAlreadyExists::create($attributes['name'], $attributes['guard_name']);\n        }\n\n        return static::query()->create($attributes);\n    }\n\n    /**\n     * A role may be given various permissions.\n     */\n    public function permissions(): BelongsToMany\n    {\n        $registrar = app(PermissionRegistrar::class);\n\n        return $this->belongsToMany(\n            config('permission.models.permission'),\n            config('permission.table_names.role_has_permissions'),\n            $registrar->pivotRole,\n            $registrar->pivotPermission\n        );\n    }\n\n    /**\n     * A role belongs to some users of the model associated with its guard.\n     */\n    public function users(): BelongsToMany\n    {\n        return $this->morphedByMany(\n            getModelForGuard($this->attributes['guard_name'] ?? config('auth.defaults.guard')),\n            'model',\n            config('permission.table_names.model_has_roles'),\n            app(PermissionRegistrar::class)->pivotRole,\n            config('permission.column_names.model_morph_key')\n        );\n    }\n\n    /**\n     * Find a role by its name and guard name.\n     *\n     * @return RoleContract|Role\n     *\n     * @throws RoleDoesNotExist\n     */\n    public static function findByName(string $name, ?string $guardName = null): RoleContract\n    {\n        $guardName ??= Guard::getDefaultName(static::class);\n\n        $role = static::findByParam(['name' => $name, 'guard_name' => $guardName]);\n\n        if (! $role) {\n            throw RoleDoesNotExist::named($name, $guardName);\n        }\n\n        return $role;\n    }\n\n    /**\n     * Find a role by its id (and optionally guardName).\n     *\n     * @return RoleContract|Role\n     */\n    public static function findById(int|string $id, ?string $guardName = null): RoleContract\n    {\n        $guardName ??= Guard::getDefaultName(static::class);\n\n        $role = static::findByParam([(new static)->getKeyName() => $id, 'guard_name' => $guardName]);\n\n        if (! $role) {\n            throw RoleDoesNotExist::withId($id, $guardName);\n        }\n\n        return $role;\n    }\n\n    /**\n     * Find or create role by its name (and optionally guardName).\n     *\n     * @return RoleContract|Role\n     */\n    public static function findOrCreate(string $name, ?string $guardName = null): RoleContract\n    {\n        $guardName ??= Guard::getDefaultName(static::class);\n\n        $attributes = ['name' => $name, 'guard_name' => $guardName];\n\n        $role = static::findByParam($attributes);\n\n        if (! $role) {\n            $registrar = app(PermissionRegistrar::class);\n            if ($registrar->teams) {\n                $teamsKey = $registrar->teamsKey;\n                $attributes[$teamsKey] = getPermissionsTeamId();\n            }\n\n            return static::query()->create($attributes);\n        }\n\n        return $role;\n    }\n\n    /**\n     * Finds a role based on an array of parameters.\n     *\n     * @return RoleContract|Role|null\n     */\n    protected static function findByParam(array $params = []): ?RoleContract\n    {\n        $query = static::query();\n\n        $registrar = app(PermissionRegistrar::class);\n\n        if ($registrar->teams) {\n            $teamsKey = $registrar->teamsKey;\n\n            $query->where(fn ($q) => $q->whereNull($teamsKey)\n                ->orWhere($teamsKey, $params[$teamsKey] ?? getPermissionsTeamId())\n            );\n            unset($params[$teamsKey]);\n        }\n\n        foreach ($params as $key => $value) {\n            $query->where($key, $value);\n        }\n\n        return $query->first();\n    }\n\n    /**\n     * Determine if the role may perform the given permission.\n     *\n     * @param  string|int|\\Spatie\\Permission\\Contracts\\Permission|BackedEnum  $permission\n     *\n     * @throws PermissionDoesNotExist|GuardDoesNotMatch\n     */\n    public function hasPermissionTo($permission, ?string $guardName = null): bool\n    {\n        if ($this->getWildcardClass()) {\n            return $this->hasWildcardPermission($permission, $guardName);\n        }\n\n        $permission = $this->filterPermission($permission, $guardName);\n\n        if (! $this->getGuardNames()->contains($permission->guard_name)) {\n            throw GuardDoesNotMatch::create($permission->guard_name, $guardName ? collect([$guardName]) : $this->getGuardNames());\n        }\n\n        return $this->loadMissing('permissions')->permissions\n            ->contains($permission->getKeyName(), $permission->getKey());\n    }\n}\n"
  },
  {
    "path": "src/PermissionRegistrar.php",
    "content": "<?php\n\nnamespace Spatie\\Permission;\n\nuse DateInterval;\nuse Illuminate\\Cache\\CacheManager;\nuse Illuminate\\Contracts\\Auth\\Access\\Authorizable;\nuse Illuminate\\Contracts\\Auth\\Access\\Gate;\nuse Illuminate\\Contracts\\Cache\\Repository;\nuse Illuminate\\Contracts\\Cache\\Store;\nuse Illuminate\\Database\\Eloquent\\Collection;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Contracts\\PermissionsTeamResolver;\nuse Spatie\\Permission\\Contracts\\Role;\n\nclass PermissionRegistrar\n{\n    protected Repository $cache;\n\n    protected CacheManager $cacheManager;\n\n    protected string $permissionClass;\n\n    protected string $roleClass;\n\n    protected Collection|array|null $permissions = null;\n\n    public string $pivotRole;\n\n    public string $pivotPermission;\n\n    public DateInterval|int $cacheExpirationTime;\n\n    public bool $teams;\n\n    protected PermissionsTeamResolver $teamResolver;\n\n    public string $teamsKey;\n\n    public string $cacheKey;\n\n    private array $cachedRoles = [];\n\n    private array $alias = [];\n\n    private array $except = [];\n\n    private array $wildcardPermissionsIndex = [];\n\n    private bool $isLoadingPermissions = false;\n\n    public function __construct(CacheManager $cacheManager)\n    {\n        $this->permissionClass = config('permission.models.permission');\n        $this->roleClass = config('permission.models.role');\n        $this->teamResolver = new (config('permission.team_resolver', DefaultTeamResolver::class));\n\n        $this->cacheManager = $cacheManager;\n        $this->initializeCache();\n    }\n\n    public function initializeCache(): void\n    {\n        $this->cacheExpirationTime = config('permission.cache.expiration_time') ?: DateInterval::createFromDateString('24 hours');\n\n        $this->teams = config('permission.teams', false);\n        $this->teamsKey = config('permission.column_names.team_foreign_key', 'team_id');\n\n        $this->cacheKey = config('permission.cache.key');\n\n        $this->pivotRole = config('permission.column_names.role_pivot_key') ?: 'role_id';\n        $this->pivotPermission = config('permission.column_names.permission_pivot_key') ?: 'permission_id';\n\n        $this->cache = $this->getCacheStoreFromConfig();\n    }\n\n    protected function getCacheStoreFromConfig(): Repository\n    {\n        // the 'default' fallback here is from the permission.php config file,\n        // where 'default' means to use config(cache.default)\n        $cacheDriver = config('permission.cache.store', 'default');\n\n        // when 'default' is specified, no action is required since we already have the default instance\n        if ($cacheDriver === 'default') {\n            return $this->cacheManager->store();\n        }\n\n        // if an undefined cache store is specified, fallback to 'array' which is Laravel's closest equiv to 'none'\n        if (! array_key_exists($cacheDriver, config('cache.stores'))) {\n            $cacheDriver = 'array';\n        }\n\n        return $this->cacheManager->store($cacheDriver);\n    }\n\n    public function setPermissionsTeamId(int|string|Model|null $id): void\n    {\n        $this->teamResolver->setPermissionsTeamId($id);\n    }\n\n    public function getPermissionsTeamId(): int|string|null\n    {\n        return $this->teamResolver->getPermissionsTeamId();\n    }\n\n    /**\n     * Register the permission check method on the gate.\n     * We resolve the Gate fresh here, for benefit of long-running instances.\n     */\n    public function registerPermissions(Gate $gate): bool\n    {\n        $gate->before(function (Authorizable $user, string $ability, array &$args = []) {\n            if (is_string($args[0] ?? null) && ! class_exists($args[0])) {\n                $guard = array_shift($args);\n            }\n            if (method_exists($user, 'checkPermissionTo')) {\n                return $user->checkPermissionTo($ability, $guard ?? null) ?: null;\n            }\n        });\n\n        return true;\n    }\n\n    /**\n     * Flush the cache.\n     */\n    public function forgetCachedPermissions(): bool\n    {\n        $this->permissions = null;\n        $this->forgetWildcardPermissionIndex();\n\n        return $this->cache->forget($this->cacheKey);\n    }\n\n    public function forgetWildcardPermissionIndex(?Model $record = null): void\n    {\n        if ($record) {\n            unset($this->wildcardPermissionsIndex[$record::class][$record->getKey()]);\n\n            return;\n        }\n\n        $this->wildcardPermissionsIndex = [];\n    }\n\n    public function getWildcardPermissionIndex(Model $record): array\n    {\n        if (isset($this->wildcardPermissionsIndex[$record::class][$record->getKey()])) {\n            return $this->wildcardPermissionsIndex[$record::class][$record->getKey()];\n        }\n\n        return $this->wildcardPermissionsIndex[$record::class][$record->getKey()] = app($record->getWildcardClass(), ['record' => $record])->getIndex();\n    }\n\n    /**\n     * Clear already-loaded permissions collection.\n     * This is only intended to be called by the PermissionServiceProvider on boot,\n     * so that long-running instances like Octane or Swoole don't keep old data in memory.\n     */\n    public function clearPermissionsCollection(): void\n    {\n        $this->permissions = null;\n        $this->wildcardPermissionsIndex = [];\n        $this->isLoadingPermissions = false;\n    }\n\n    /**\n     * Load permissions from cache\n     * And turns permissions array into a \\Illuminate\\Database\\Eloquent\\Collection\n     *\n     * Thread-safe implementation to prevent race conditions in concurrent environments\n     * (e.g., Laravel Octane, Swoole, parallel requests)\n     */\n    private function loadPermissions(int $retries = 0): void\n    {\n        // First check (without lock) - fast path for already loaded permissions\n        if ($this->permissions) {\n            return;\n        }\n\n        // Prevent concurrent loading using a flag-based lock\n        // This protects against cache stampede and duplicate database queries\n        if ($this->isLoadingPermissions && $retries < 10) {\n            // Another thread is loading, wait and retry\n            usleep(10000); // Wait 10ms\n            $retries++;\n\n            // After wait, recursively check again if permissions were loaded\n            $this->loadPermissions($retries);\n\n            return;\n        }\n\n        // Set loading flag to prevent concurrent loads\n        $this->isLoadingPermissions = true;\n\n        try {\n            $this->permissions = $this->cache->remember(\n                $this->cacheKey, $this->cacheExpirationTime, fn () => $this->getSerializedPermissionsForCache()\n            );\n\n            $this->alias = $this->permissions['alias'];\n\n            $this->hydrateRolesCache();\n\n            $this->permissions = $this->getHydratedPermissionCollection();\n\n            $this->cachedRoles = $this->alias = $this->except = [];\n        } finally {\n            // Always release the loading flag, even if an exception occurs\n            $this->isLoadingPermissions = false;\n        }\n    }\n\n    /**\n     * Get the permissions based on the passed params.\n     */\n    public function getPermissions(array $params = [], bool $onlyOne = false): Collection\n    {\n        $this->loadPermissions();\n\n        $method = $onlyOne ? 'first' : 'filter';\n\n        $permissions = $this->permissions->$method(static function ($permission) use ($params) {\n            return array_all($params, fn ($value, $attr) => $permission->getAttribute($attr) == $value);\n\n        });\n\n        if ($onlyOne) {\n            $permissions = new Collection($permissions ? [$permissions] : []);\n        }\n\n        return $permissions;\n    }\n\n    public function getPermissionClass(): string\n    {\n        return $this->permissionClass;\n    }\n\n    public function setPermissionClass(string $permissionClass): static\n    {\n        $this->permissionClass = $permissionClass;\n        config()->set('permission.models.permission', $permissionClass);\n        app()->bind(Permission::class, $permissionClass);\n\n        return $this;\n    }\n\n    public function getRoleClass(): string\n    {\n        return $this->roleClass;\n    }\n\n    public function setRoleClass(string $roleClass): static\n    {\n        $this->roleClass = $roleClass;\n        config()->set('permission.models.role', $roleClass);\n        app()->bind(Role::class, $roleClass);\n\n        return $this;\n    }\n\n    public function getCacheRepository(): Repository\n    {\n        return $this->cache;\n    }\n\n    public function getCacheStore(): Store\n    {\n        return $this->cache->getStore();\n    }\n\n    protected function getPermissionsWithRoles(): Collection\n    {\n        return $this->permissionClass::select()->with('roles')->get();\n    }\n\n    /**\n     * Changes array keys with alias\n     */\n    private function aliasedArray(array|Model $model): array\n    {\n        return collect(is_array($model) ? $model : $model->getAttributes())->except($this->except)\n            ->keyBy(fn ($value, $key) => $this->alias[$key] ?? $key)\n            ->all();\n    }\n\n    /**\n     * Array for cache alias\n     */\n    private function aliasModelFields(Model $newKeys): void\n    {\n        $i = 0;\n        $alphas = ! count($this->alias) ? range('a', 'h') : range('j', 'p');\n\n        foreach (array_keys($newKeys->getAttributes()) as $value) {\n            if (! isset($this->alias[$value])) {\n                $this->alias[$value] = $alphas[$i++] ?? $value;\n            }\n        }\n\n        $this->alias = array_diff_key($this->alias, array_flip($this->except));\n    }\n\n    /*\n     * Make the cache smaller using an array with only required fields\n     */\n    private function getSerializedPermissionsForCache(): array\n    {\n        $this->except = config('permission.cache.column_names_except', ['created_at', 'updated_at', 'deleted_at']);\n\n        $permissions = $this->getPermissionsWithRoles()\n            ->map(function ($permission) {\n                if (! $this->alias) {\n                    $this->aliasModelFields($permission);\n                }\n\n                return $this->aliasedArray($permission) + $this->getSerializedRoleRelation($permission);\n            })->all();\n        $roles = array_values($this->cachedRoles);\n        $this->cachedRoles = [];\n\n        return ['alias' => array_flip($this->alias)] + compact('permissions', 'roles');\n    }\n\n    private function getSerializedRoleRelation(Model $permission): array\n    {\n        if (! $permission->roles->count()) {\n            return [];\n        }\n\n        if (! isset($this->alias['roles'])) {\n            $this->alias['roles'] = 'r';\n            $this->aliasModelFields($permission->roles[0]);\n        }\n\n        return [\n            'r' => $permission->roles->map(function ($role) {\n                if (! isset($this->cachedRoles[$role->getKey()])) {\n                    $this->cachedRoles[$role->getKey()] = $this->aliasedArray($role);\n                }\n\n                return $role->getKey();\n            })->all(),\n        ];\n    }\n\n    private function getHydratedPermissionCollection(): Collection\n    {\n        $permissionInstance = (new ($this->getPermissionClass())())->newInstance([], true);\n\n        return Collection::make(array_map(\n            fn ($item) => (clone $permissionInstance)\n                ->setRawAttributes($this->aliasedArray(array_diff_key($item, ['r' => 0])), true)\n                ->setRelation('roles', $this->getHydratedRoleCollection($item['r'] ?? [])),\n            $this->permissions['permissions']\n        ));\n    }\n\n    private function getHydratedRoleCollection(array $roles): Collection\n    {\n        return Collection::make(array_values(\n            array_intersect_key($this->cachedRoles, array_flip($roles))\n        ));\n    }\n\n    private function hydrateRolesCache(): void\n    {\n        $roleInstance = (new ($this->getRoleClass())())->newInstance([], true);\n\n        array_map(function ($item) use ($roleInstance) {\n            $role = (clone $roleInstance)\n                ->setRawAttributes($this->aliasedArray($item), true);\n            $this->cachedRoles[$role->getKey()] = $role;\n        }, $this->permissions['roles']);\n\n        $this->permissions['roles'] = [];\n    }\n\n    public static function isUid(mixed $value): bool\n    {\n        if (! is_string($value) || empty(trim($value))) {\n            return false;\n        }\n\n        // check if is UUID/GUID\n        $uid = preg_match('/^[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}$/iD', $value) > 0;\n        if ($uid) {\n            return true;\n        }\n\n        // check if is ULID\n        $ulid = strlen($value) === 26 && strspn($value, '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz') === 26 && $value[0] <= '7';\n        if ($ulid) {\n            return true;\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/PermissionServiceProvider.php",
    "content": "<?php\n\nnamespace Spatie\\Permission;\n\nuse Composer\\InstalledVersions;\nuse Illuminate\\Contracts\\Auth\\Access\\Gate;\nuse Illuminate\\Contracts\\Events\\Dispatcher;\nuse Illuminate\\Contracts\\Foundation\\Application;\nuse Illuminate\\Foundation\\Console\\AboutCommand;\nuse Illuminate\\Routing\\Route;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\View\\Compilers\\BladeCompiler;\nuse Laravel\\Octane\\Contracts\\OperationTerminated;\nuse Spatie\\LaravelPackageTools\\Package;\nuse Spatie\\LaravelPackageTools\\PackageServiceProvider;\nuse Spatie\\Permission\\Contracts\\Permission as PermissionContract;\nuse Spatie\\Permission\\Contracts\\Role as RoleContract;\n\nuse function Illuminate\\Support\\enum_value;\n\nclass PermissionServiceProvider extends PackageServiceProvider\n{\n    public function configurePackage(Package $package): void\n    {\n        $package\n            ->name('laravel-permission')\n            ->hasConfigFile('permission')\n            ->hasMigrations(['create_permission_tables'])\n            ->hasCommands([\n                Commands\\CacheResetCommand::class,\n                Commands\\CreateRoleCommand::class,\n                Commands\\CreatePermissionCommand::class,\n                Commands\\ShowCommand::class,\n                Commands\\UpgradeForTeamsCommand::class,\n                Commands\\AssignRoleCommand::class,\n            ]);\n    }\n\n    public function registeringPackage(): void\n    {\n        $this->callAfterResolving('blade.compiler', fn (BladeCompiler $bladeCompiler) => $this->registerBladeExtensions($bladeCompiler));\n    }\n\n    public function packageBooted(): void\n    {\n        $this->registerMacroHelpers();\n        $this->registerModelBindings();\n        $this->registerOctaneListener();\n\n        $this->callAfterResolving(Gate::class, function (Gate $gate, Application $app) {\n            if ($this->app['config']->get('permission.register_permission_check_method')) {\n                $permissionLoader = $app->get(PermissionRegistrar::class);\n                $permissionLoader->clearPermissionsCollection();\n                $permissionLoader->registerPermissions($gate);\n            }\n        });\n\n        $this->app->singleton(PermissionRegistrar::class);\n        $this->registerAbout();\n    }\n\n    public static function bladeMethodWrapper(string $method, mixed $role, ?string $guard = null): bool\n    {\n        return auth($guard)->check() && auth($guard)->user()->{$method}($role);\n    }\n\n    protected function registerBladeExtensions(BladeCompiler $bladeCompiler): void\n    {\n        $bladeMethodWrapper = '\\\\Spatie\\\\Permission\\\\PermissionServiceProvider::bladeMethodWrapper';\n\n        // permission checks\n        $bladeCompiler->if('haspermission', fn () => $bladeMethodWrapper('checkPermissionTo', ...func_get_args()));\n\n        // role checks\n        $bladeCompiler->if('role', fn () => $bladeMethodWrapper('hasRole', ...func_get_args()));\n        $bladeCompiler->if('hasrole', fn () => $bladeMethodWrapper('hasRole', ...func_get_args()));\n        $bladeCompiler->if('hasanyrole', fn () => $bladeMethodWrapper('hasAnyRole', ...func_get_args()));\n        $bladeCompiler->if('hasallroles', fn () => $bladeMethodWrapper('hasAllRoles', ...func_get_args()));\n        $bladeCompiler->if('hasexactroles', fn () => $bladeMethodWrapper('hasExactRoles', ...func_get_args()));\n        $bladeCompiler->directive('endunlessrole', fn () => '<?php endif; ?>');\n    }\n\n    protected function registerModelBindings(): void\n    {\n        $this->app->bind(PermissionContract::class, fn ($app) => $app->make($app->config['permission.models.permission']));\n        $this->app->bind(RoleContract::class, fn ($app) => $app->make($app->config['permission.models.role']));\n    }\n\n    protected function registerMacroHelpers(): void\n    {\n        Route::macro('role', function ($roles = []) {\n            $roles = Arr::wrap($roles);\n            $roles = array_map(fn ($role) => enum_value($role), $roles);\n\n            /** @var Route $this */\n            return $this->middleware('role:'.implode('|', $roles));\n        });\n\n        Route::macro('permission', function ($permissions = []) {\n            $permissions = Arr::wrap($permissions);\n            $permissions = array_map(fn ($permission) => enum_value($permission), $permissions);\n\n            /** @var Route $this */\n            return $this->middleware('permission:'.implode('|', $permissions));\n        });\n\n        Route::macro('roleOrPermission', function ($rolesOrPermissions = []) {\n            $rolesOrPermissions = Arr::wrap($rolesOrPermissions);\n            $rolesOrPermissions = array_map(fn ($item) => enum_value($item), $rolesOrPermissions);\n\n            /** @var Route $this */\n            return $this->middleware('role_or_permission:'.implode('|', $rolesOrPermissions));\n        });\n    }\n\n    protected function registerOctaneListener(): void\n    {\n        if ($this->app->runningInConsole() || ! $this->app['config']->get('octane.listeners')) {\n            return;\n        }\n\n        $dispatcher = $this->app[Dispatcher::class];\n        // @phpstan-ignore-next-line\n        $dispatcher->listen(function (OperationTerminated $event) {\n            // @phpstan-ignore-next-line\n            $event->sandbox->make(PermissionRegistrar::class)->setPermissionsTeamId(null);\n        });\n\n        if (! $this->app['config']->get('permission.register_octane_reset_listener')) {\n            return;\n        }\n        // @phpstan-ignore-next-line\n        $dispatcher->listen(function (OperationTerminated $event) {\n            // @phpstan-ignore-next-line\n            $event->sandbox->make(PermissionRegistrar::class)->clearPermissionsCollection();\n        });\n    }\n\n    protected function registerAbout(): void\n    {\n        if (! class_exists(InstalledVersions::class) || ! class_exists(AboutCommand::class)) {\n            return;\n        }\n\n        // array format: 'Display Text' => 'boolean-config-key name'\n        $features = [\n            'Teams' => 'teams',\n            'Wildcard-Permissions' => 'enable_wildcard_permission',\n            'Octane-Listener' => 'register_octane_reset_listener',\n            'Passport' => 'use_passport_client_credentials',\n        ];\n\n        $config = $this->app['config'];\n\n        AboutCommand::add('Spatie Permissions', static fn () => [\n            'Features Enabled' => collect($features)\n                ->filter(fn (string $feature, string $name): bool => $config->get(\"permission.{$feature}\"))\n                ->keys()\n                ->whenEmpty(fn (Collection $collection) => $collection->push('Default'))\n                ->join(', '),\n            'Version' => InstalledVersions::getPrettyVersion('spatie/laravel-permission'),\n        ]);\n    }\n}\n"
  },
  {
    "path": "src/Traits/HasPermissions.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Traits;\n\nuse BackedEnum;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Collection;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Contracts\\Wildcard;\nuse Spatie\\Permission\\Events\\PermissionAttachedEvent;\nuse Spatie\\Permission\\Events\\PermissionDetachedEvent;\nuse Spatie\\Permission\\Exceptions\\GuardDoesNotMatch;\nuse Spatie\\Permission\\Exceptions\\PermissionDoesNotExist;\nuse Spatie\\Permission\\Exceptions\\WildcardPermissionInvalidArgument;\nuse Spatie\\Permission\\Exceptions\\WildcardPermissionNotImplementsContract;\nuse Spatie\\Permission\\Guard;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\WildcardPermission;\n\nuse function Illuminate\\Support\\enum_value;\n\ntrait HasPermissions\n{\n    private ?string $permissionClass = null;\n\n    private ?string $wildcardClass = null;\n\n    private array $wildcardPermissionsIndex;\n\n    public static function bootHasPermissions(): void\n    {\n        static::deleting(function ($model) {\n            if (method_exists($model, 'isForceDeleting') && ! $model->isForceDeleting()) {\n                return;\n            }\n\n            $teams = app(PermissionRegistrar::class)->teams;\n            app(PermissionRegistrar::class)->teams = false;\n            if (! $model instanceof Permission) {\n                $model->permissions()->detach();\n            }\n            if ($model instanceof Role) {\n                $model->users()->detach();\n            }\n            app(PermissionRegistrar::class)->teams = $teams;\n        });\n    }\n\n    public function getPermissionClass(): string\n    {\n        if (! $this->permissionClass) {\n            $this->permissionClass = app(PermissionRegistrar::class)->getPermissionClass();\n        }\n\n        return $this->permissionClass;\n    }\n\n    public function getWildcardClass(): string\n    {\n        if (! is_null($this->wildcardClass)) {\n            return $this->wildcardClass;\n        }\n\n        $this->wildcardClass = '';\n\n        if (config('permission.enable_wildcard_permission')) {\n            $this->wildcardClass = config('permission.wildcard_permission', WildcardPermission::class);\n\n            if (! is_subclass_of($this->wildcardClass, Wildcard::class)) {\n                throw WildcardPermissionNotImplementsContract::create();\n            }\n        }\n\n        return $this->wildcardClass;\n    }\n\n    /**\n     * A model may have multiple direct permissions.\n     */\n    public function permissions(): BelongsToMany\n    {\n        $relation = $this->morphToMany(\n            config('permission.models.permission'),\n            'model',\n            config('permission.table_names.model_has_permissions'),\n            config('permission.column_names.model_morph_key'),\n            app(PermissionRegistrar::class)->pivotPermission\n        );\n\n        if (! app(PermissionRegistrar::class)->teams) {\n            return $relation;\n        }\n\n        $teamsKey = app(PermissionRegistrar::class)->teamsKey;\n        $relation->withPivot($teamsKey);\n\n        return $relation->wherePivot($teamsKey, getPermissionsTeamId());\n    }\n\n    /**\n     * Scope the model query to certain permissions only.\n     *\n     * @param  string|int|array|Permission|Collection|BackedEnum  $permissions\n     */\n    public function scopePermission(Builder $query, $permissions, bool $without = false): Builder\n    {\n        $permissions = $this->convertToPermissionModels($permissions);\n\n        $permissionKey = (new ($this->getPermissionClass())())->getKeyName();\n        $roleKey = (new ($this instanceof Role ? static::class : $this->getRoleClass())())->getKeyName();\n\n        $rolesWithPermissions = $this instanceof Role ? [] : array_unique(\n            array_reduce($permissions, fn ($result, $permission) => array_merge($result, $permission->roles->all()), [])\n        );\n\n        return $query->where(fn (Builder $query) => $query\n            ->{! $without ? 'whereHas' : 'whereDoesntHave'}('permissions', fn (Builder $subQuery) => $subQuery\n            ->whereIn(config('permission.table_names.permissions').\".$permissionKey\", array_column($permissions, $permissionKey))\n            )\n            ->when(count($rolesWithPermissions), fn ($whenQuery) => $whenQuery\n                ->{! $without ? 'orWhereHas' : 'whereDoesntHave'}('roles', fn (Builder $subQuery) => $subQuery\n                ->whereIn(config('permission.table_names.roles').\".$roleKey\", array_column($rolesWithPermissions, $roleKey))\n                )\n            )\n        );\n    }\n\n    /**\n     * Scope the model query to only those without certain permissions,\n     * whether indirectly by role or by direct permission.\n     *\n     * @param  string|int|array|Permission|Collection|BackedEnum  $permissions\n     */\n    public function scopeWithoutPermission(Builder $query, $permissions): Builder\n    {\n        return $this->scopePermission($query, $permissions, true);\n    }\n\n    /**\n     * @param  string|int|array|Permission|Collection|BackedEnum  $permissions\n     *\n     * @throws PermissionDoesNotExist\n     */\n    protected function convertToPermissionModels($permissions): array\n    {\n        if ($permissions instanceof Collection) {\n            $permissions = $permissions->all();\n        }\n\n        return array_map(function ($permission) {\n            if ($permission instanceof Permission) {\n                return $permission;\n            }\n\n            $permission = enum_value($permission);\n\n            $method = is_int($permission) || PermissionRegistrar::isUid($permission) ? 'findById' : 'findByName';\n\n            return $this->getPermissionClass()::{$method}($permission, $this->getDefaultGuardName());\n        }, Arr::wrap($permissions));\n    }\n\n    /**\n     * Find a permission.\n     *\n     * @param  string|int|Permission|BackedEnum  $permission\n     *\n     * @throws PermissionDoesNotExist\n     */\n    public function filterPermission($permission, ?string $guardName = null): Permission\n    {\n        $permission = enum_value($permission);\n\n        if (is_int($permission) || PermissionRegistrar::isUid($permission)) {\n            $permission = $this->getPermissionClass()::findById(\n                $permission,\n                $guardName ?? $this->getDefaultGuardName()\n            );\n        }\n\n        if (is_string($permission)) {\n            $permission = $this->getPermissionClass()::findByName(\n                $permission,\n                $guardName ?? $this->getDefaultGuardName()\n            );\n        }\n\n        if (! $permission instanceof Permission) {\n            throw new PermissionDoesNotExist;\n        }\n\n        return $permission;\n    }\n\n    /**\n     * Determine if the model may perform the given permission.\n     *\n     * @param  string|int|Permission|BackedEnum  $permission\n     *\n     * @throws PermissionDoesNotExist\n     */\n    public function hasPermissionTo($permission, ?string $guardName = null): bool\n    {\n        if ($this->getWildcardClass()) {\n            return $this->hasWildcardPermission($permission, $guardName);\n        }\n\n        $permission = $this->filterPermission($permission, $guardName);\n\n        return $this->hasDirectPermission($permission) || $this->hasPermissionViaRole($permission);\n    }\n\n    /**\n     * Validates a wildcard permission against all permissions of a user.\n     *\n     * @param  string|int|Permission|BackedEnum  $permission\n     */\n    protected function hasWildcardPermission($permission, ?string $guardName = null): bool\n    {\n        $guardName = $guardName ?? $this->getDefaultGuardName();\n\n        $permission = enum_value($permission);\n\n        if (is_int($permission) || PermissionRegistrar::isUid($permission)) {\n            $permission = $this->getPermissionClass()::findById($permission, $guardName);\n        }\n\n        if ($permission instanceof Permission) {\n            $guardName = $permission->guard_name ?? $guardName;\n            $permission = $permission->name;\n        }\n\n        if (! is_string($permission)) {\n            throw WildcardPermissionInvalidArgument::create();\n        }\n\n        return app($this->getWildcardClass(), ['record' => $this])->implies(\n            $permission,\n            $guardName,\n            app(PermissionRegistrar::class)->getWildcardPermissionIndex($this),\n        );\n    }\n\n    /**\n     * An alias to hasPermissionTo(), but avoids throwing an exception.\n     *\n     * @param  string|int|Permission|BackedEnum  $permission\n     */\n    public function checkPermissionTo($permission, ?string $guardName = null): bool\n    {\n        try {\n            return $this->hasPermissionTo($permission, $guardName);\n        } catch (PermissionDoesNotExist $e) {\n            return false;\n        }\n    }\n\n    /**\n     * Determine if the model has any of the given permissions.\n     *\n     * @param  string|int|array|Permission|Collection|BackedEnum  ...$permissions\n     */\n    public function hasAnyPermission(...$permissions): bool\n    {\n        $permissions = collect($permissions)->flatten();\n\n        foreach ($permissions as $permission) {\n            if ($this->checkPermissionTo($permission)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Determine if the model has all of the given permissions.\n     *\n     * @param  string|int|array|Permission|Collection|BackedEnum  ...$permissions\n     */\n    public function hasAllPermissions(...$permissions): bool\n    {\n        $permissions = collect($permissions)->flatten();\n\n        foreach ($permissions as $permission) {\n            if (! $this->checkPermissionTo($permission)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Determine if the model has, via roles, the given permission.\n     */\n    protected function hasPermissionViaRole(Permission $permission): bool\n    {\n        if ($this instanceof Role) {\n            return false;\n        }\n\n        return $this->hasRole($permission->roles);\n    }\n\n    /**\n     * Determine if the model has the given permission.\n     *\n     * @param  string|int|Permission|BackedEnum  $permission\n     *\n     * @throws PermissionDoesNotExist\n     */\n    public function hasDirectPermission($permission): bool\n    {\n        $permission = $this->filterPermission($permission);\n\n        return $this->loadMissing('permissions')->permissions\n            ->contains($permission->getKeyName(), $permission->getKey());\n    }\n\n    /**\n     * Return all the permissions the model has via roles.\n     */\n    public function getPermissionsViaRoles(): Collection\n    {\n        if ($this instanceof Role || $this instanceof Permission) {\n            return collect();\n        }\n\n        return $this->loadMissing('roles', 'roles.permissions')\n            ->roles->flatMap(fn ($role) => $role->permissions)\n            ->sort()->values();\n    }\n\n    /**\n     * Return all the permissions the model has, both directly and via roles.\n     */\n    public function getAllPermissions(): Collection\n    {\n        /** @var Collection $permissions */\n        $permissions = $this->permissions;\n\n        if (! $this instanceof Permission) {\n            $permissions = $permissions->merge($this->getPermissionsViaRoles());\n        }\n\n        return $permissions->sort()->values();\n    }\n\n    /**\n     * Returns array of permissions ids\n     *\n     * @param  string|int|array|Permission|Collection|BackedEnum  $permissions\n     */\n    private function collectPermissions(...$permissions): array\n    {\n        return collect($permissions)\n            ->flatten()\n            ->reduce(function ($array, $permission) {\n                if ($permission === null || $permission === '') {\n                    return $array;\n                }\n\n                $permission = $this->getStoredPermission($permission);\n                if (! $permission instanceof Permission) {\n                    return $array;\n                }\n\n                if (! in_array($permission->getKey(), $array)) {\n                    $this->ensureModelSharesGuard($permission);\n                    $array[] = $permission->getKey();\n                }\n\n                return $array;\n            }, []);\n    }\n\n    /**\n     * Grant the given permission(s) to a role.\n     *\n     * @param  string|int|array|Permission|Collection|BackedEnum  $permissions\n     * @return $this\n     */\n    public function givePermissionTo(...$permissions): static\n    {\n        $permissions = $this->collectPermissions($permissions);\n\n        $model = $this->getModel();\n        $teamPivot = app(PermissionRegistrar::class)->teams && ! $this instanceof Role ?\n            [app(PermissionRegistrar::class)->teamsKey => getPermissionsTeamId()] : [];\n\n        if ($model->exists) {\n            $currentPermissions = $this->permissions->map(fn ($permission) => $permission->getKey())->toArray();\n\n            $this->permissions()->attach(array_diff($permissions, $currentPermissions), $teamPivot);\n            $model->unsetRelation('permissions');\n        } else {\n            $class = $model::class;\n            $saved = false;\n\n            $class::saved(\n                function ($object) use ($permissions, $model, $teamPivot, &$saved) {\n                    if ($saved || $model->getKey() != $object->getKey()) {\n                        return;\n                    }\n                    $model->permissions()->attach($permissions, $teamPivot);\n                    $model->unsetRelation('permissions');\n                    $saved = true;\n                }\n            );\n        }\n\n        if ($this instanceof Role) {\n            $this->forgetCachedPermissions();\n        }\n\n        if (config('permission.events_enabled')) {\n            event(new PermissionAttachedEvent($this->getModel(), $permissions));\n        }\n\n        $this->forgetWildcardPermissionIndex();\n\n        return $this;\n    }\n\n    public function forgetWildcardPermissionIndex(): void\n    {\n        app(PermissionRegistrar::class)->forgetWildcardPermissionIndex(\n            $this instanceof Role ? null : $this,\n        );\n    }\n\n    /**\n     * Remove all current permissions and set the given ones.\n     *\n     * @param  string|int|array|Permission|Collection|BackedEnum  $permissions\n     * @return $this\n     */\n    public function syncPermissions(...$permissions): static\n    {\n        if ($this->getModel()->exists) {\n            $this->collectPermissions($permissions);\n            $this->permissions()->detach();\n            $this->setRelation('permissions', collect());\n        }\n\n        return $this->givePermissionTo($permissions);\n    }\n\n    /**\n     * Revoke the given permission(s).\n     *\n     * @param  Permission|Permission[]|string|string[]|BackedEnum  $permission\n     * @return $this\n     */\n    public function revokePermissionTo($permission): static\n    {\n        $storedPermission = $this->getStoredPermission($permission);\n\n        $this->permissions()->detach($storedPermission);\n\n        if ($this instanceof Role) {\n            $this->forgetCachedPermissions();\n        }\n\n        if (config('permission.events_enabled')) {\n            event(new PermissionDetachedEvent($this->getModel(), $storedPermission));\n        }\n\n        $this->forgetWildcardPermissionIndex();\n\n        $this->unsetRelation('permissions');\n\n        return $this;\n    }\n\n    public function getPermissionNames(): Collection\n    {\n        return $this->permissions->pluck('name');\n    }\n\n    /**\n     * @param  string|int|array|Permission|Collection|BackedEnum  $permissions\n     * @return Permission|Permission[]|Collection\n     */\n    protected function getStoredPermission($permissions)\n    {\n        $permissions = enum_value($permissions);\n\n        if (is_int($permissions) || PermissionRegistrar::isUid($permissions)) {\n            return $this->getPermissionClass()::findById($permissions, $this->getDefaultGuardName());\n        }\n\n        if (is_string($permissions)) {\n            return $this->getPermissionClass()::findByName($permissions, $this->getDefaultGuardName());\n        }\n\n        if (is_array($permissions)) {\n            $permissions = array_map(fn ($permission) => $permission instanceof Permission ? $permission->name : enum_value($permission), $permissions);\n\n            return $this->getPermissionClass()::whereIn('name', $permissions)\n                ->whereIn('guard_name', $this->getGuardNames())\n                ->get();\n        }\n\n        return $permissions;\n    }\n\n    /**\n     * @param  Permission|Role  $roleOrPermission\n     *\n     * @throws GuardDoesNotMatch\n     */\n    protected function ensureModelSharesGuard($roleOrPermission): void\n    {\n        if (! $this->getGuardNames()->contains($roleOrPermission->guard_name)) {\n            throw GuardDoesNotMatch::create($roleOrPermission->guard_name, $this->getGuardNames());\n        }\n    }\n\n    protected function getGuardNames(): Collection\n    {\n        return Guard::getNames($this);\n    }\n\n    protected function getDefaultGuardName(): string\n    {\n        return Guard::getDefaultName($this);\n    }\n\n    /**\n     * Forget the cached permissions.\n     */\n    public function forgetCachedPermissions(): void\n    {\n        app(PermissionRegistrar::class)->forgetCachedPermissions();\n    }\n\n    /**\n     * Check if the model has All of the requested Direct permissions.\n     *\n     * @param  string|int|array|Permission|Collection|BackedEnum  ...$permissions\n     */\n    public function hasAllDirectPermissions(...$permissions): bool\n    {\n        $permissions = collect($permissions)->flatten();\n\n        foreach ($permissions as $permission) {\n            if (! $this->hasDirectPermission($permission)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Check if the model has Any of the requested Direct permissions.\n     *\n     * @param  string|int|array|Permission|Collection|BackedEnum  ...$permissions\n     */\n    public function hasAnyDirectPermission(...$permissions): bool\n    {\n        $permissions = collect($permissions)->flatten();\n\n        foreach ($permissions as $permission) {\n            if ($this->hasDirectPermission($permission)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/Traits/HasRoles.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Traits;\n\nuse BackedEnum;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Collection;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Events\\RoleAttachedEvent;\nuse Spatie\\Permission\\Events\\RoleDetachedEvent;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse TypeError;\n\nuse function Illuminate\\Support\\enum_value;\n\ntrait HasRoles\n{\n    use HasPermissions;\n\n    private ?string $roleClass = null;\n\n    public static function bootHasRoles(): void\n    {\n        static::deleting(function ($model) {\n            if (method_exists($model, 'isForceDeleting') && ! $model->isForceDeleting()) {\n                return;\n            }\n\n            $teams = app(PermissionRegistrar::class)->teams;\n            app(PermissionRegistrar::class)->teams = false;\n            $model->roles()->detach();\n            if ($model instanceof Permission) {\n                $model->users()->detach();\n            }\n            app(PermissionRegistrar::class)->teams = $teams;\n        });\n    }\n\n    public function getRoleClass(): string\n    {\n        if (! $this->roleClass) {\n            $this->roleClass = app(PermissionRegistrar::class)->getRoleClass();\n        }\n\n        return $this->roleClass;\n    }\n\n    /**\n     * A model may have multiple roles.\n     */\n    public function roles(): BelongsToMany\n    {\n        $relation = $this->morphToMany(\n            config('permission.models.role'),\n            'model',\n            config('permission.table_names.model_has_roles'),\n            config('permission.column_names.model_morph_key'),\n            app(PermissionRegistrar::class)->pivotRole\n        );\n\n        if (! app(PermissionRegistrar::class)->teams) {\n            return $relation;\n        }\n\n        $teamsKey = app(PermissionRegistrar::class)->teamsKey;\n        $relation->withPivot($teamsKey);\n        $teamField = config('permission.table_names.roles').'.'.$teamsKey;\n\n        return $relation->wherePivot($teamsKey, getPermissionsTeamId())\n            ->where(fn ($q) => $q->whereNull($teamField)->orWhere($teamField, getPermissionsTeamId()));\n    }\n\n    /**\n     * Scope the model query to certain roles only.\n     *\n     * @param  string|int|array|Role|Collection|BackedEnum  $roles\n     */\n    public function scopeRole(Builder $query, $roles, ?string $guard = null, bool $without = false): Builder\n    {\n        if ($roles instanceof Collection) {\n            $roles = $roles->all();\n        }\n\n        $roles = array_map(function ($role) use ($guard) {\n            if ($role instanceof Role) {\n                return $role;\n            }\n\n            $role = enum_value($role);\n\n            $method = is_int($role) || PermissionRegistrar::isUid($role) ? 'findById' : 'findByName';\n\n            return $this->getRoleClass()::{$method}($role, $guard ?: $this->getDefaultGuardName());\n        }, Arr::wrap($roles));\n\n        $key = (new ($this->getRoleClass())())->getKeyName();\n\n        return $query->{! $without ? 'whereHas' : 'whereDoesntHave'}('roles', fn (Builder $subQuery) => $subQuery\n            ->whereIn(config('permission.table_names.roles').\".$key\", array_column($roles, $key))\n        );\n    }\n\n    /**\n     * Scope the model query to only those without certain roles.\n     *\n     * @param  string|int|array|Role|Collection|BackedEnum  $roles\n     */\n    public function scopeWithoutRole(Builder $query, $roles, ?string $guard = null): Builder\n    {\n        return $this->scopeRole($query, $roles, $guard, true);\n    }\n\n    /**\n     * Returns array of role ids\n     *\n     * @param  string|int|array|Role|Collection|BackedEnum  $roles\n     */\n    private function collectRoles(...$roles): array\n    {\n        return collect($roles)\n            ->flatten()\n            ->reduce(function ($array, $role) {\n                if ($role === null || $role === '') {\n                    return $array;\n                }\n\n                $role = $this->getStoredRole($role);\n\n                if (! in_array($role->getKey(), $array)) {\n                    $this->ensureModelSharesGuard($role);\n                    $array[] = $role->getKey();\n                }\n\n                return $array;\n            }, []);\n    }\n\n    /**\n     * Assign the given role to the model.\n     *\n     * @param  string|int|array|Role|Collection|BackedEnum  ...$roles\n     * @return $this\n     */\n    public function assignRole(...$roles): static\n    {\n        $roles = $this->collectRoles($roles);\n\n        $model = $this->getModel();\n        $teamPivot = app(PermissionRegistrar::class)->teams && ! $this instanceof Permission ?\n            [app(PermissionRegistrar::class)->teamsKey => getPermissionsTeamId()] : [];\n\n        if ($model->exists) {\n            if (app(PermissionRegistrar::class)->teams) {\n                // explicit reload in case team has been changed since last load\n                $this->load('roles');\n            }\n\n            $currentRoles = $this->roles->map(fn ($role) => $role->getKey())->toArray();\n\n            $this->roles()->attach(array_diff($roles, $currentRoles), $teamPivot);\n            $model->unsetRelation('roles');\n        } else {\n            $class = $model::class;\n            $saved = false;\n\n            $class::saved(\n                function ($object) use ($roles, $model, $teamPivot, &$saved) {\n                    if ($saved || $model->getKey() != $object->getKey()) {\n                        return;\n                    }\n                    $model->roles()->attach($roles, $teamPivot);\n                    $model->unsetRelation('roles');\n                    $saved = true;\n                }\n            );\n        }\n\n        if ($this instanceof Permission) {\n            $this->forgetCachedPermissions();\n        }\n\n        $this->forgetWildcardPermissionIndex();\n\n        if (config('permission.events_enabled')) {\n            event(new RoleAttachedEvent($this->getModel(), $roles));\n        }\n\n        return $this;\n    }\n\n    /**\n     * Revoke the given role from the model.\n     *\n     * @param  string|int|array|Role|Collection|BackedEnum  ...$role\n     * @return $this\n     */\n    public function removeRole(...$role): static\n    {\n        $roles = $this->collectRoles($role);\n\n        $this->roles()->detach($roles);\n\n        $this->unsetRelation('roles');\n\n        if ($this instanceof Permission) {\n            $this->forgetCachedPermissions();\n        }\n\n        $this->forgetWildcardPermissionIndex();\n\n        if (config('permission.events_enabled')) {\n            event(new RoleDetachedEvent($this->getModel(), $roles));\n        }\n\n        return $this;\n    }\n\n    /**\n     * Remove all current roles and set the given ones.\n     *\n     * @param  string|int|array|Role|Collection|BackedEnum  ...$roles\n     * @return $this\n     */\n    public function syncRoles(...$roles): static\n    {\n        if ($this->getModel()->exists) {\n            $this->collectRoles($roles);\n            if (config('permission.events_enabled')) {\n                $currentRoles = $this->roles()->get();\n                if ($currentRoles->isNotEmpty()) {\n                    $this->removeRole($currentRoles);\n                }\n            } else {\n                $this->roles()->detach();\n                $this->setRelation('roles', collect());\n            }\n        }\n\n        return $this->assignRole($roles);\n    }\n\n    /**\n     * Determine if the model has (one of) the given role(s).\n     *\n     * @param  string|int|array|Role|Collection|BackedEnum  $roles\n     */\n    public function hasRole($roles, ?string $guard = null): bool\n    {\n        $this->loadMissing('roles');\n\n        if (is_string($roles) && str_contains($roles, '|')) {\n            $roles = $this->convertPipeToArray($roles);\n        }\n\n        if ($roles instanceof BackedEnum) {\n            $roles = $roles->value;\n\n            return $this->roles\n                ->when($guard, fn ($q) => $q->where('guard_name', $guard))\n                ->pluck('name')\n                ->contains(fn ($name) => enum_value($name) == $roles);\n        }\n\n        if (is_int($roles) || PermissionRegistrar::isUid($roles)) {\n            $key = (new ($this->getRoleClass())())->getKeyName();\n\n            return $guard\n                ? $this->roles->where('guard_name', $guard)->contains($key, $roles)\n                : $this->roles->contains($key, $roles);\n        }\n\n        if (is_string($roles)) {\n            return $guard\n                ? $this->roles->where('guard_name', $guard)->contains('name', $roles)\n                : $this->roles->contains('name', $roles);\n        }\n\n        if ($roles instanceof Role) {\n            return $this->roles->contains($roles->getKeyName(), $roles->getKey());\n        }\n\n        if (is_array($roles)) {\n            foreach ($roles as $role) {\n                if ($this->hasRole($role, $guard)) {\n                    return true;\n                }\n            }\n\n            return false;\n        }\n\n        if ($roles instanceof Collection) {\n            return $roles->intersect($guard ? $this->roles->where('guard_name', $guard) : $this->roles)->isNotEmpty();\n        }\n\n        throw new TypeError('Unsupported type for $roles parameter to hasRole().');\n    }\n\n    /**\n     * Determine if the model has any of the given role(s).\n     *\n     * Alias to hasRole() but without Guard controls\n     *\n     * @param  string|int|array|Role|Collection|BackedEnum  $roles\n     */\n    public function hasAnyRole(...$roles): bool\n    {\n        return $this->hasRole($roles);\n    }\n\n    /**\n     * Determine if the model has all of the given role(s).\n     *\n     * @param  string|array|Role|Collection|BackedEnum  $roles\n     */\n    public function hasAllRoles($roles, ?string $guard = null): bool\n    {\n        $this->loadMissing('roles');\n\n        $roles = enum_value($roles);\n\n        if (is_string($roles) && str_contains($roles, '|')) {\n            $roles = $this->convertPipeToArray($roles);\n        }\n\n        if (is_string($roles)) {\n            return $this->hasRole($roles, $guard);\n        }\n\n        if ($roles instanceof Role) {\n            return $this->roles->contains($roles->getKeyName(), $roles->getKey());\n        }\n\n        $roles = collect()->make($roles)->map(fn ($role) => $role instanceof Role ? $role->name : enum_value($role));\n\n        $roleNames = $guard\n            ? $this->roles->where('guard_name', $guard)->pluck('name')\n            : $this->getRoleNames();\n\n        $roleNames = $roleNames->transform(fn ($roleName) => enum_value($roleName));\n\n        return $roles->intersect($roleNames) == $roles;\n    }\n\n    /**\n     * Determine if the model has exactly all of the given role(s).\n     *\n     * @param  string|array|Role|Collection|BackedEnum  $roles\n     */\n    public function hasExactRoles($roles, ?string $guard = null): bool\n    {\n        $this->loadMissing('roles');\n\n        if (is_string($roles) && str_contains($roles, '|')) {\n            $roles = $this->convertPipeToArray($roles);\n        }\n\n        if (is_string($roles)) {\n            $roles = [$roles];\n        }\n\n        if ($roles instanceof Role) {\n            $roles = [$roles->name];\n        }\n\n        $roles = collect()->make($roles)->map(fn ($role) => $role instanceof Role ? $role->name : $role\n        );\n\n        return $this->roles->count() == $roles->count() && $this->hasAllRoles($roles, $guard);\n    }\n\n    /**\n     * Return all permissions directly coupled to the model.\n     */\n    public function getDirectPermissions(): Collection\n    {\n        return $this->permissions;\n    }\n\n    public function getRoleNames(): Collection\n    {\n        $this->loadMissing('roles');\n\n        return $this->roles->pluck('name');\n    }\n\n    protected function getStoredRole($role): Role\n    {\n        $role = enum_value($role);\n\n        if (is_int($role) || PermissionRegistrar::isUid($role)) {\n            return $this->getRoleClass()::findById($role, $this->getDefaultGuardName());\n        }\n\n        if (is_string($role)) {\n            return $this->getRoleClass()::findByName($role, $this->getDefaultGuardName());\n        }\n\n        return $role;\n    }\n\n    protected function convertPipeToArray(string $pipeString): array\n    {\n        $pipeString = trim($pipeString);\n\n        if (strlen($pipeString) <= 2) {\n            return [str_replace('|', '', $pipeString)];\n        }\n\n        $quoteCharacter = substr($pipeString, 0, 1);\n        $endCharacter = substr($quoteCharacter, -1, 1);\n\n        if ($quoteCharacter !== $endCharacter) {\n            return explode('|', $pipeString);\n        }\n\n        if (! in_array($quoteCharacter, [\"'\", '\"'])) {\n            return explode('|', $pipeString);\n        }\n\n        return explode('|', trim($pipeString, $quoteCharacter));\n    }\n}\n"
  },
  {
    "path": "src/Traits/RefreshesPermissionCache.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Traits;\n\nuse Spatie\\Permission\\PermissionRegistrar;\n\ntrait RefreshesPermissionCache\n{\n    public static function bootRefreshesPermissionCache()\n    {\n        static::saved(function () {\n            app(PermissionRegistrar::class)->forgetCachedPermissions();\n        });\n\n        static::deleted(function () {\n            app(PermissionRegistrar::class)->forgetCachedPermissions();\n        });\n    }\n}\n"
  },
  {
    "path": "src/WildcardPermission.php",
    "content": "<?php\n\nnamespace Spatie\\Permission;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Support\\Str;\nuse Spatie\\Permission\\Contracts\\Wildcard;\nuse Spatie\\Permission\\Exceptions\\WildcardPermissionNotProperlyFormatted;\n\nclass WildcardPermission implements Wildcard\n{\n    /** @var string */\n    public const WILDCARD_TOKEN = '*';\n\n    /** @var non-empty-string */\n    public const PART_DELIMITER = '.';\n\n    /** @var non-empty-string */\n    public const SUBPART_DELIMITER = ',';\n\n    public function __construct(protected Model $record) {}\n\n    public function getIndex(): array\n    {\n        $index = [];\n\n        foreach ($this->record->getAllPermissions() as $permission) {\n            $index[$permission->guard_name] = $this->buildIndex(\n                $index[$permission->guard_name] ?? [],\n                explode(static::PART_DELIMITER, $permission->name),\n                $permission->name,\n            );\n        }\n\n        return $index;\n    }\n\n    protected function buildIndex(array $index, array $parts, string $permission): array\n    {\n        if (empty($parts)) {\n            $index[''] = true;\n\n            return $index;\n        }\n\n        $part = array_shift($parts);\n\n        if (blank($part)) {\n            throw WildcardPermissionNotProperlyFormatted::create($permission);\n        }\n\n        if (! Str::contains($part, static::SUBPART_DELIMITER)) {\n            $index[$part] = $this->buildIndex(\n                $index[$part] ?? [],\n                $parts,\n                $permission,\n            );\n        }\n\n        $subParts = explode(static::SUBPART_DELIMITER, $part);\n\n        foreach ($subParts as $subPart) {\n            if (blank($subPart)) {\n                throw WildcardPermissionNotProperlyFormatted::create($permission);\n            }\n\n            $index[$subPart] = $this->buildIndex(\n                $index[$subPart] ?? [],\n                $parts,\n                $permission,\n            );\n        }\n\n        return $index;\n    }\n\n    public function implies(string $permission, string $guardName, array $index): bool\n    {\n        if (! array_key_exists($guardName, $index)) {\n            return false;\n        }\n\n        $permission = explode(static::PART_DELIMITER, $permission);\n\n        return $this->checkIndex($permission, $index[$guardName]);\n    }\n\n    protected function checkIndex(array $permission, array $index): bool\n    {\n        if (array_key_exists(strval(null), $index)) {\n            return true;\n        }\n\n        if (empty($permission)) {\n            return false;\n        }\n\n        $firstPermission = array_shift($permission);\n\n        if (\n            array_key_exists($firstPermission, $index) &&\n            $this->checkIndex($permission, $index[$firstPermission])\n        ) {\n            return true;\n        }\n\n        if (array_key_exists(static::WILDCARD_TOKEN, $index)) {\n            return $this->checkIndex($permission, $index[static::WILDCARD_TOKEN]);\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/helpers.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Spatie\\Permission\\Guard;\nuse Spatie\\Permission\\PermissionRegistrar;\n\nif (! function_exists('getModelForGuard')) {\n    function getModelForGuard(string $guard): ?string\n    {\n        return Guard::getModelForGuard($guard);\n    }\n}\n\nif (! function_exists('setPermissionsTeamId')) {\n    function setPermissionsTeamId(int|string|Model|null $id): void\n    {\n        app(PermissionRegistrar::class)->setPermissionsTeamId($id);\n    }\n}\n\nif (! function_exists('getPermissionsTeamId')) {\n    function getPermissionsTeamId(): int|string|null\n    {\n        return app(PermissionRegistrar::class)->getPermissionsTeamId();\n    }\n}\n"
  },
  {
    "path": "tests/Commands/CommandTest.php",
    "content": "<?php\n\nuse Composer\\InstalledVersions;\nuse Illuminate\\Foundation\\Console\\AboutCommand;\nuse Illuminate\\Support\\Facades\\Artisan;\nuse Spatie\\Permission\\Models\\Permission;\nuse Spatie\\Permission\\Models\\Role;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nit('can create a role', function () {\n    Artisan::call('permission:create-role', ['name' => 'new-role']);\n\n    expect(Role::where('name', 'new-role')->get())->toHaveCount(1);\n    expect(Role::where('name', 'new-role')->first()->permissions)->toHaveCount(0);\n});\n\nit('can create a role with a specific guard', function () {\n    Artisan::call('permission:create-role', [\n        'name' => 'new-role',\n        'guard' => 'api',\n    ]);\n\n    expect(Role::where('name', 'new-role')->where('guard_name', 'api')->get())->toHaveCount(1);\n});\n\nit('can create a permission', function () {\n    Artisan::call('permission:create-permission', ['name' => 'new-permission']);\n\n    expect(Permission::where('name', 'new-permission')->get())->toHaveCount(1);\n});\n\nit('can create a permission with a specific guard', function () {\n    Artisan::call('permission:create-permission', [\n        'name' => 'new-permission',\n        'guard' => 'api',\n    ]);\n\n    expect(Permission::where('name', 'new-permission')->where('guard_name', 'api')->get())->toHaveCount(1);\n});\n\nit('can create a role and permissions at same time', function () {\n    Artisan::call('permission:create-role', [\n        'name' => 'new-role',\n        'permissions' => 'first permission | second permission',\n    ]);\n\n    $role = Role::where('name', 'new-role')->first();\n\n    expect($role->hasPermissionTo('first permission'))->toBeTrue();\n    expect($role->hasPermissionTo('second permission'))->toBeTrue();\n});\n\nit('can create a role without duplication', function () {\n    Artisan::call('permission:create-role', ['name' => 'new-role']);\n    Artisan::call('permission:create-role', ['name' => 'new-role']);\n\n    expect(Role::where('name', 'new-role')->get())->toHaveCount(1);\n    expect(Role::where('name', 'new-role')->first()->permissions)->toHaveCount(0);\n});\n\nit('can create a permission without duplication', function () {\n    Artisan::call('permission:create-permission', ['name' => 'new-permission']);\n    Artisan::call('permission:create-permission', ['name' => 'new-permission']);\n\n    expect(Permission::where('name', 'new-permission')->get())->toHaveCount(1);\n});\n\nit('can show permission tables', function () {\n    Role::where('name', 'testRole2')->delete();\n    Role::create(['name' => 'testRole_2']);\n\n    Artisan::call('permission:show');\n\n    $output = Artisan::output();\n\n    expect(strpos($output, 'Guard: web'))->not->toBeFalse();\n    expect(strpos($output, 'Guard: admin'))->not->toBeFalse();\n\n    expect($output)->toMatch('/\\|\\s+\\|\\s+testRole\\s+\\|\\s+testRole_2\\s+\\|/');\n    expect($output)->toMatch('/\\|\\s+edit-articles\\s+\\|\\s+·\\s+\\|\\s+·\\s+\\|/');\n\n    Role::findByName('testRole')->givePermissionTo('edit-articles');\n    $this->reloadPermissions();\n\n    Artisan::call('permission:show');\n\n    $output = Artisan::output();\n\n    expect($output)->toMatch('/\\|\\s+edit-articles\\s+\\|\\s+✔\\s+\\|\\s+·\\s+\\|/');\n});\n\nit('can show permissions for guard', function () {\n    Artisan::call('permission:show', ['guard' => 'web']);\n\n    $output = Artisan::output();\n\n    expect(strpos($output, 'Guard: web'))->not->toBeFalse();\n    expect(strpos($output, 'Guard: admin'))->toBeFalse();\n});\n\nit('can setup teams upgrade', function () {\n    config()->set('permission.teams', true);\n\n    $this->artisan('permission:setup-teams')\n        ->expectsQuestion('Proceed with the migration creation?', 'yes')\n        ->assertExitCode(0);\n\n    $matchingFiles = glob(database_path('migrations/*_add_teams_fields.php'));\n    expect(count($matchingFiles) > 0)->toBeTrue();\n\n    $AddTeamsFields = require $matchingFiles[count($matchingFiles) - 1];\n    $AddTeamsFields->up();\n    $AddTeamsFields->up(); // test upgrade teams migration fresh\n\n    Role::create(['name' => 'new-role', 'team_test_id' => 1]);\n    $role = Role::where('name', 'new-role')->first();\n    expect($role)->not->toBeNull();\n    expect((int) $role->team_test_id)->toBe(1);\n\n    // remove migration\n    foreach ($matchingFiles as $file) {\n        unlink($file);\n    }\n});\n\nit('can show roles by teams', function () {\n    config()->set('permission.teams', true);\n    app(\\Spatie\\Permission\\PermissionRegistrar::class)->initializeCache();\n\n    Role::where('name', 'testRole2')->delete();\n    Role::create(['name' => 'testRole_2']);\n    Role::create(['name' => 'testRole_Team', 'team_test_id' => 1]);\n    Role::create(['name' => 'testRole_Team', 'team_test_id' => 2]); // same name different team\n    Artisan::call('permission:show');\n\n    $output = Artisan::output();\n\n    expect($output)->toMatch('/\\|\\s+\\|\\s+Team ID: NULL\\s+\\|\\s+Team ID: 1\\s+\\|\\s+Team ID: 2\\s+\\|/');\n    expect($output)->toMatch('/\\|\\s+\\|\\s+testRole\\s+\\|\\s+testRole_2\\s+\\|\\s+testRole_Team\\s+\\|\\s+testRole_Team\\s+\\|/');\n});\n\nit('can respond to about command with default', function () {\n    if (! class_exists(InstalledVersions::class) || ! class_exists(AboutCommand::class)) {\n        $this->markTestSkipped();\n    }\n    if (! method_exists(AboutCommand::class, 'flushState')) {\n        $this->markTestSkipped();\n    }\n\n    app(\\Spatie\\Permission\\PermissionRegistrar::class)->initializeCache();\n\n    Artisan::call('about');\n    $output = str_replace(\"\\r\\n\", \"\\n\", Artisan::output());\n\n    $pattern = '/Spatie Permissions[ .\\n]*Features Enabled[ .]*Default[ .\\n]*Version/';\n    expect($output)->toMatch($pattern);\n});\n\nit('can respond to about command with teams', function () {\n    if (! class_exists(InstalledVersions::class) || ! class_exists(AboutCommand::class)) {\n        $this->markTestSkipped();\n    }\n    if (! method_exists(AboutCommand::class, 'flushState')) {\n        $this->markTestSkipped();\n    }\n\n    app(\\Spatie\\Permission\\PermissionRegistrar::class)->initializeCache();\n\n    config()->set('permission.teams', true);\n\n    Artisan::call('about');\n    $output = str_replace(\"\\r\\n\", \"\\n\", Artisan::output());\n\n    $pattern = '/Spatie Permissions[ .\\n]*Features Enabled[ .]*Teams[ .\\n]*Version/';\n    expect($output)->toMatch($pattern);\n});\n\nit('can assign role to user', function () {\n    $user = User::first();\n\n    Artisan::call('permission:assign-role', [\n        'name' => 'testRole',\n        'userId' => $user->id,\n        'guard' => 'web',\n        'userModelNamespace' => User::class,\n    ]);\n\n    $output = Artisan::output();\n\n    expect($output)->toContain('Role `testRole` assigned to user ID '.$user->id.' successfully.');\n    expect(Role::where('name', 'testRole')->get())->toHaveCount(1);\n    expect($user->roles)->toHaveCount(1);\n    expect($user->hasRole('testRole'))->toBeTrue();\n});\n\nit('fails to assign role when user not found', function () {\n    Artisan::call('permission:assign-role', [\n        'name' => 'testRole',\n        'userId' => 99999,\n        'guard' => 'web',\n        'userModelNamespace' => User::class,\n    ]);\n\n    $output = Artisan::output();\n\n    expect($output)->toContain('User with ID 99999 not found.');\n});\n\nit('fails to assign role when namespace invalid', function () {\n    $user = User::first();\n\n    $userModelClass = 'App\\Models\\NonExistentUser';\n\n    Artisan::call('permission:assign-role', [\n        'name' => 'testRole',\n        'userId' => $user->id,\n        'guard' => 'web',\n        'userModelNamespace' => $userModelClass,\n    ]);\n\n    $output = Artisan::output();\n\n    expect($output)->toContain(\"User model class [{$userModelClass}] does not exist.\");\n});\n\nit('warns when assigning role with team id but teams disabled', function () {\n    $user = User::first();\n\n    Artisan::call('permission:assign-role', [\n        'name' => 'testRole',\n        'userId' => $user->id,\n        'userModelNamespace' => User::class,\n        '--team-id' => 1,\n    ]);\n\n    $output = Artisan::output();\n\n    expect($output)->toContain('Teams feature disabled');\n});\n"
  },
  {
    "path": "tests/Commands/TeamCommandTest.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Facades\\Artisan;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nbeforeEach(fn () => $this->setUpTeams());\n\nit('can assign role to user with team id', function () {\n    $user = User::first();\n\n    Artisan::call('permission:assign-role', [\n        'name' => 'testRole',\n        'userId' => $user->id,\n        'guard' => 'web',\n        'userModelNamespace' => User::class,\n        '--team-id' => 1,\n    ]);\n\n    $output = Artisan::output();\n\n    expect($output)->toContain('Role `testRole` assigned to user ID '.$user->id.' successfully.');\n\n    setPermissionsTeamId(1);\n    $user->unsetRelation('roles');\n    expect($user->hasRole('testRole'))->toBeTrue();\n});\n\nit('can assign role to user on different teams', function () {\n    $user = User::first();\n\n    Artisan::call('permission:assign-role', [\n        'name' => 'testRole',\n        'userId' => $user->id,\n        'guard' => 'web',\n        'userModelNamespace' => User::class,\n        '--team-id' => 1,\n    ]);\n\n    Artisan::call('permission:assign-role', [\n        'name' => 'testRole2',\n        'userId' => $user->id,\n        'guard' => 'web',\n        'userModelNamespace' => User::class,\n        '--team-id' => 2,\n    ]);\n\n    setPermissionsTeamId(1);\n    $user->unsetRelation('roles');\n    expect($user->hasRole('testRole'))->toBeTrue();\n    expect($user->hasRole('testRole2'))->toBeFalse();\n\n    setPermissionsTeamId(2);\n    $user->unsetRelation('roles');\n    expect($user->hasRole('testRole2'))->toBeTrue();\n    expect($user->hasRole('testRole'))->toBeFalse();\n});\n\nit('restores previous team id after assigning role', function () {\n    $user = User::first();\n\n    setPermissionsTeamId(5);\n\n    Artisan::call('permission:assign-role', [\n        'name' => 'testRole',\n        'userId' => $user->id,\n        'guard' => 'web',\n        'userModelNamespace' => User::class,\n        '--team-id' => 1,\n    ]);\n\n    expect(getPermissionsTeamId())->toEqual(5);\n});\n"
  },
  {
    "path": "tests/Integration/BladeTest.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Facades\\Artisan;\nuse Spatie\\Permission\\Contracts\\Role;\n\nfunction renderView(string $view, array $parameters): string\n{\n    Artisan::call('view:clear');\n\n    return trim((string) view($view)->with($parameters));\n}\n\nbeforeEach(function () {\n    $roleModel = app(Role::class);\n\n    $roleModel->create(['name' => 'member']);\n    $roleModel->create(['name' => 'writer']);\n    $roleModel->create(['name' => 'intern']);\n    $roleModel->create(['name' => 'super-admin', 'guard_name' => 'admin']);\n    $roleModel->create(['name' => 'moderator', 'guard_name' => 'admin']);\n\n    $this->getWriter = function () {\n        $this->testUser->assignRole('writer');\n\n        return $this->testUser;\n    };\n\n    $this->getMember = function () {\n        $this->testUser->assignRole('member');\n\n        return $this->testUser;\n    };\n\n    $this->getSuperAdmin = function () {\n        $this->testAdmin->assignRole('super-admin');\n\n        return $this->testAdmin;\n    };\n});\n\nit('evaluates all blade directives as false when there is nobody logged in', function () {\n    $permission = 'edit-articles';\n    $role = 'writer';\n    $roles = [$role];\n    $elserole = 'na';\n    $elsepermission = 'na';\n\n    expect(renderView('can', ['permission' => $permission]))->toEqual('does not have permission');\n    expect(renderView('haspermission', compact('permission', 'elsepermission')))->toEqual('does not have permission');\n    expect(renderView('role', compact('role', 'elserole')))->toEqual('does not have role');\n    expect(renderView('hasRole', compact('role', 'elserole')))->toEqual('does not have role');\n    expect(renderView('hasAllRoles', compact('roles')))->toEqual('does not have all of the given roles');\n    expect(renderView('hasAllRoles', ['roles' => implode('|', $roles)]))->toEqual('does not have all of the given roles');\n    expect(renderView('hasAnyRole', compact('roles')))->toEqual('does not have any of the given roles');\n    expect(renderView('hasAnyRole', ['roles' => implode('|', $roles)]))->toEqual('does not have any of the given roles');\n});\n\nit('evaluates all blade directives as false when somebody without roles or permissions is logged in', function () {\n    $permission = 'edit-articles';\n    $role = 'writer';\n    $roles = 'writer';\n    $elserole = 'na';\n    $elsepermission = 'na';\n\n    auth()->setUser($this->testUser);\n\n    expect(renderView('can', compact('permission')))->toEqual('does not have permission');\n    expect(renderView('haspermission', compact('permission', 'elsepermission')))->toEqual('does not have permission');\n    expect(renderView('role', compact('role', 'elserole')))->toEqual('does not have role');\n    expect(renderView('hasRole', compact('role', 'elserole')))->toEqual('does not have role');\n    expect(renderView('hasAllRoles', compact('roles')))->toEqual('does not have all of the given roles');\n    expect(renderView('hasAnyRole', compact('roles')))->toEqual('does not have any of the given roles');\n});\n\nit('evaluates all blade directives as false when somebody with another guard is logged in', function () {\n    $permission = 'edit-articles';\n    $role = 'writer';\n    $roles = 'writer';\n    $elserole = 'na';\n    $elsepermission = 'na';\n\n    auth('admin')->setUser($this->testAdmin);\n\n    expect(renderView('can', compact('permission')))->toEqual('does not have permission');\n    expect(renderView('haspermission', compact('permission', 'elsepermission')))->toEqual('does not have permission');\n    expect(renderView('role', compact('role', 'elserole')))->toEqual('does not have role');\n    expect(renderView('hasRole', compact('role', 'elserole')))->toEqual('does not have role');\n    expect(renderView('hasAllRoles', compact('roles')))->toEqual('does not have all of the given roles');\n    expect(renderView('hasAnyRole', compact('roles')))->toEqual('does not have any of the given roles');\n    expect(renderView('hasAnyRole', compact('roles')))->toEqual('does not have any of the given roles');\n});\n\nit('accepts a guard name in the can directive', function () {\n    $user = ($this->getWriter)();\n    $user->givePermissionTo('edit-articles');\n    auth()->setUser($user);\n\n    $permission = 'edit-articles';\n    $guard = 'web';\n    expect(renderView('can', compact('permission', 'guard')))->toEqual('has permission');\n    $guard = 'admin';\n    expect(renderView('can', compact('permission', 'guard')))->toEqual('does not have permission');\n\n    auth()->logout();\n\n    // log in as the Admin with the permission-via-role\n    $this->testAdmin->givePermissionTo($this->testAdminPermission);\n    $user = $this->testAdmin;\n    auth()->setUser($user);\n\n    $permission = 'edit-articles';\n    $guard = 'web';\n    expect(renderView('can', compact('permission', 'guard')))->toEqual('does not have permission');\n\n    $permission = 'admin-permission';\n    $guard = 'admin';\n    expect($this->testAdmin->checkPermissionTo($permission, $guard))->toBeTrue();\n    expect(renderView('can', compact('permission', 'guard')))->toEqual('has permission');\n});\n\nit('evaluates the can directive as true when the logged in user has the permission', function () {\n    $user = ($this->getWriter)();\n    $user->givePermissionTo('edit-articles');\n\n    auth()->setUser($user);\n\n    expect(renderView('can', ['permission' => 'edit-articles']))->toEqual('has permission');\n});\n\nit('evaluates the haspermission directive as true when the logged in user has the permission', function () {\n    $user = ($this->getWriter)();\n\n    $permission = 'edit-articles';\n    $user->givePermissionTo('edit-articles');\n\n    auth()->setUser($user);\n\n    expect(renderView('haspermission', compact('permission')))->toEqual('has permission');\n\n    $guard = 'admin';\n    $elsepermission = 'na';\n    expect(renderView('haspermission', compact('permission', 'elsepermission', 'guard')))->toEqual('does not have permission');\n\n    $this->testAdminRole->givePermissionTo($this->testAdminPermission);\n    $this->testAdmin->assignRole($this->testAdminRole);\n    auth('admin')->setUser($this->testAdmin);\n    $guard = 'admin';\n    $permission = 'admin-permission';\n    expect(renderView('haspermission', compact('permission', 'guard', 'elsepermission')))->toEqual('has permission');\n});\n\nit('evaluates the role directive as true when the logged in user has the role', function () {\n    auth()->setUser(($this->getWriter)());\n\n    expect(renderView('role', ['role' => 'writer', 'elserole' => 'na']))->toEqual('has role');\n});\n\nit('evaluates the elserole directive as true when the logged in user has the role', function () {\n    auth()->setUser(($this->getMember)());\n\n    expect(renderView('role', ['role' => 'writer', 'elserole' => 'member']))->toEqual('has else role');\n});\n\nit('evaluates the role directive as true when the logged in user has the role for the given guard', function () {\n    auth('admin')->setUser(($this->getSuperAdmin)());\n\n    expect(renderView('guardRole', ['role' => 'super-admin', 'guard' => 'admin']))->toEqual('has role for guard');\n});\n\nit('evaluates the hasrole directive as true when the logged in user has the role', function () {\n    auth()->setUser(($this->getWriter)());\n\n    expect(renderView('hasRole', ['role' => 'writer']))->toEqual('has role');\n});\n\nit('evaluates the hasrole directive as true when the logged in user has the role for the given guard', function () {\n    auth('admin')->setUser(($this->getSuperAdmin)());\n\n    expect(renderView('guardHasRole', ['role' => 'super-admin', 'guard' => 'admin']))->toEqual('has role');\n});\n\nit('evaluates the unlessrole directive as true when the logged in user does not have the role', function () {\n    auth()->setUser(($this->getWriter)());\n\n    expect(renderView('unlessrole', ['role' => 'another']))->toEqual('does not have role');\n});\n\nit('evaluates the unlessrole directive as true when the logged in user does not have the role for the given guard', function () {\n    auth('admin')->setUser(($this->getSuperAdmin)());\n\n    expect(renderView('guardunlessrole', ['role' => 'another', 'guard' => 'admin']))->toEqual('does not have role');\n    expect(renderView('guardunlessrole', ['role' => 'super-admin', 'guard' => 'web']))->toEqual('does not have role');\n});\n\nit('evaluates the hasanyrole directive as false when the logged in user does not have any of the required roles', function () {\n    $roles = ['writer', 'intern'];\n\n    auth()->setUser(($this->getMember)());\n\n    expect(renderView('hasAnyRole', compact('roles')))->toEqual('does not have any of the given roles');\n    expect(renderView('hasAnyRole', ['roles' => implode('|', $roles)]))->toEqual('does not have any of the given roles');\n});\n\nit('evaluates the hasanyrole directive as true when the logged in user does have some of the required roles', function () {\n    $roles = ['member', 'writer', 'intern'];\n\n    auth()->setUser(($this->getMember)());\n\n    expect(renderView('hasAnyRole', compact('roles')))->toEqual('does have some of the roles');\n    expect(renderView('hasAnyRole', ['roles' => implode('|', $roles)]))->toEqual('does have some of the roles');\n});\n\nit('evaluates the hasanyrole directive as true when the logged in user does have some of the required roles for the given guard', function () {\n    $roles = ['super-admin', 'moderator'];\n    $guard = 'admin';\n\n    auth('admin')->setUser(($this->getSuperAdmin)());\n\n    expect(renderView('guardHasAnyRole', compact('roles', 'guard')))->toEqual('does have some of the roles');\n});\n\nit('evaluates the hasanyrole directive as true when the logged in user does have some of the required roles in pipe', function () {\n    $guard = 'admin';\n\n    auth('admin')->setUser(($this->getSuperAdmin)());\n\n    expect(renderView('guardHasAnyRolePipe', compact('guard')))->toEqual('does have some of the roles');\n});\n\nit('evaluates the hasanyrole directive as false when the logged in user doesnt have some of the required roles in pipe', function () {\n    $guard = '';\n\n    auth('admin')->setUser(($this->getMember)());\n\n    expect(renderView('guardHasAnyRolePipe', compact('guard')))->toEqual('does not have any of the given roles');\n});\n\nit('evaluates the hasallroles directive as false when the logged in user does not have all required roles', function () {\n    $roles = ['member', 'writer'];\n\n    auth()->setUser(($this->getMember)());\n\n    expect(renderView('hasAllRoles', compact('roles')))->toEqual('does not have all of the given roles');\n    expect(renderView('hasAllRoles', ['roles' => implode('|', $roles)]))->toEqual('does not have all of the given roles');\n});\n\nit('evaluates the hasallroles directive as true when the logged in user does have all required roles', function () {\n    $roles = ['member', 'writer'];\n\n    $user = ($this->getMember)();\n    $user->assignRole('writer');\n\n    auth()->setUser($user);\n\n    expect(renderView('hasAllRoles', compact('roles')))->toEqual('does have all of the given roles');\n    expect(renderView('hasAllRoles', ['roles' => implode('|', $roles)]))->toEqual('does have all of the given roles');\n});\n\nit('evaluates the hasallroles directive as true when the logged in user does have all required roles for the given guard', function () {\n    $roles = ['super-admin', 'moderator'];\n    $guard = 'admin';\n\n    $admin = ($this->getSuperAdmin)();\n    $admin->assignRole('moderator');\n\n    auth('admin')->setUser($admin);\n\n    expect(renderView('guardHasAllRoles', compact('roles', 'guard')))->toEqual('does have all of the given roles');\n});\n\nit('evaluates the hasallroles directive as true when the logged in user does have all required roles in pipe', function () {\n    $guard = 'admin';\n\n    $admin = ($this->getSuperAdmin)();\n    $admin->assignRole('moderator');\n\n    auth('admin')->setUser($admin);\n\n    expect(renderView('guardHasAllRolesPipe', compact('guard')))->toEqual('does have all of the given roles');\n});\n\nit('evaluates the hasallroles directive as false when the logged in user doesnt have all required roles in pipe', function () {\n    $guard = '';\n    $user = ($this->getMember)();\n    $user->assignRole('writer');\n\n    auth()->setUser($user);\n\n    expect(renderView('guardHasAllRolesPipe', compact('guard')))->toEqual('does not have all of the given roles');\n});\n\nit('evaluates the hasallroles directive as true when the logged in user does have all required roles in array', function () {\n    $guard = 'admin';\n\n    $admin = ($this->getSuperAdmin)();\n    $admin->assignRole('moderator');\n\n    auth('admin')->setUser($admin);\n\n    expect(renderView('guardHasAllRolesArray', compact('guard')))->toEqual('does have all of the given roles');\n});\n\nit('evaluates the hasallroles directive as false when the logged in user doesnt have all required roles in array', function () {\n    $guard = '';\n    $user = ($this->getMember)();\n    $user->assignRole('writer');\n\n    auth()->setUser($user);\n\n    expect(renderView('guardHasAllRolesArray', compact('guard')))->toEqual('does not have all of the given roles');\n});\n"
  },
  {
    "path": "tests/Integration/CacheTest.php",
    "content": "<?php\n\nuse Illuminate\\Cache\\DatabaseStore;\nuse Illuminate\\Support\\Facades\\Artisan;\nuse Illuminate\\Support\\Facades\\DB;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Exceptions\\PermissionDoesNotExist;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nbeforeEach(function () {\n    $this->registrar = app(PermissionRegistrar::class);\n\n    $this->registrar->forgetCachedPermissions();\n\n    DB::connection()->enableQueryLog();\n\n    $this->cache_init_count = 0;\n    $this->cache_load_count = 0;\n    $this->cache_run_count = 2; // roles lookup, permissions lookup\n\n    $cacheStore = $this->registrar->getCacheStore();\n\n    switch (true) {\n        case $cacheStore instanceof DatabaseStore:\n            $this->cache_init_count = 1;\n            $this->cache_load_count = 1;\n            // no break\n        default:\n    }\n});\n\nfunction resetQueryCount(): void\n{\n    DB::flushQueryLog();\n}\n\nfunction assertQueryCount(int $expected): void\n{\n    expect(DB::getQueryLog())->toHaveCount($expected);\n}\n\nit('can cache the permissions', function () {\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n});\n\nit('flushes the cache when creating a permission', function () {\n    app(Permission::class)->create(['name' => 'new']);\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n});\n\nit('flushes the cache when updating a permission', function () {\n    $permission = app(Permission::class)->create(['name' => 'new']);\n\n    $permission->name = 'other name';\n    $permission->save();\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n});\n\nit('flushes the cache when creating a role', function () {\n    app(Role::class)->create(['name' => 'new']);\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n});\n\nit('flushes the cache when updating a role', function () {\n    $role = app(Role::class)->create(['name' => 'new']);\n\n    $role->name = 'other name';\n    $role->save();\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n});\n\nit('should not flush the cache when removing a permission from a user', function () {\n    $this->testUser->givePermissionTo('edit-articles');\n\n    $this->registrar->getPermissions();\n\n    $this->testUser->revokePermissionTo('edit-articles');\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount(0);\n});\n\nit('should not flush the cache when removing a role from a user', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->registrar->getPermissions();\n\n    $this->testUser->removeRole('testRole');\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount(0);\n});\n\nit('flushes the cache when removing a role from a permission', function () {\n    $this->testUserPermission->assignRole('testRole');\n\n    $this->registrar->getPermissions();\n\n    $this->testUserPermission->removeRole('testRole');\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n});\n\nit('flushes the cache when assigning a permission to a role', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n});\n\nit('should not flush the cache on user creation', function () {\n    $this->registrar->getPermissions();\n\n    User::create(['email' => 'new']);\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    // should all be in memory, so no init/load required\n    assertQueryCount(0);\n});\n\nit('flushes the cache when giving a permission to a role', function () {\n    $this->testUserRole->givePermissionTo($this->testUserPermission);\n\n    resetQueryCount();\n\n    $this->registrar->getPermissions();\n\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n});\n\nit('uses the cache for has permission to', function () {\n    $this->testUserRole->givePermissionTo(['edit-articles', 'edit-news', 'Edit News']);\n    $this->testUser->assignRole('testRole');\n    $this->testUser->loadMissing('roles', 'permissions');\n\n    resetQueryCount();\n    expect($this->testUser->hasPermissionTo('edit-articles'))->toBeTrue();\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n\n    resetQueryCount();\n    expect($this->testUser->hasPermissionTo('edit-news'))->toBeTrue();\n    assertQueryCount(0);\n\n    resetQueryCount();\n    expect($this->testUser->hasPermissionTo('edit-articles'))->toBeTrue();\n    assertQueryCount(0);\n\n    resetQueryCount();\n    expect($this->testUser->hasPermissionTo('Edit News'))->toBeTrue();\n    assertQueryCount(0);\n});\n\nit('differentiates the cache by guard name', function () {\n    expect(function () {\n        $this->testUserRole->givePermissionTo(['edit-articles', 'web']);\n        $this->testUser->assignRole('testRole');\n        $this->testUser->loadMissing('roles', 'permissions');\n\n        resetQueryCount();\n        expect($this->testUser->hasPermissionTo('edit-articles', 'web'))->toBeTrue();\n        assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n\n        resetQueryCount();\n        expect($this->testUser->hasPermissionTo('edit-articles', 'admin'))->toBeFalse();\n        assertQueryCount(1); // 1 for first lookup of this permission with this guard\n    })->toThrow(PermissionDoesNotExist::class);\n});\n\nit('uses the cache for get all permissions', function () {\n    $this->testUserRole->givePermissionTo($expected = ['edit-articles', 'edit-news']);\n    $this->testUser->assignRole('testRole');\n    $this->testUser->loadMissing('roles.permissions', 'permissions');\n\n    resetQueryCount();\n    $this->registrar->getPermissions();\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n\n    resetQueryCount();\n    $actual = $this->testUser->getAllPermissions()->pluck('name')->sort()->values();\n    expect($actual)->toEqual(collect($expected));\n\n    assertQueryCount(0);\n});\n\nit('should not over hydrate roles for get all permissions', function () {\n    $this->testUserRole->givePermissionTo(['edit-articles', 'edit-news']);\n    $permissions = $this->registrar->getPermissions();\n    $roles = $permissions->flatMap->roles;\n\n    // Should have same object reference\n    expect($roles[1])->toBe($roles[0]);\n});\n\nit('can reset the cache with artisan command', function () {\n    Artisan::call('permission:create-permission', ['name' => 'new-permission']);\n    expect(\\Spatie\\Permission\\Models\\Permission::where('name', 'new-permission')->get())->toHaveCount(1);\n\n    resetQueryCount();\n    // retrieve permissions, and assert that the cache had to be loaded\n    $this->registrar->getPermissions();\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n\n    // reset the cache\n    Artisan::call('permission:cache-reset');\n\n    resetQueryCount();\n    $this->registrar->getPermissions();\n    // assert that the cache had to be reloaded\n    assertQueryCount($this->cache_init_count + $this->cache_load_count + $this->cache_run_count);\n});\n"
  },
  {
    "path": "tests/Integration/CustomGateTest.php",
    "content": "<?php\n\nuse Illuminate\\Contracts\\Auth\\Access\\Gate;\n\nbeforeEach(function () {\n    config()->set('permission.register_permission_check_method', false);\n    app()->forgetInstance(Gate::class);\n});\n\nit('doesnt register the method for checking permissions on the gate', function () {\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect(app(Gate::class)->abilities())->toBeEmpty();\n    expect($this->testUser->can('edit-articles'))->toBeFalse();\n});\n\nit('can authorize using custom method for checking permissions', function () {\n    app(Gate::class)->define('edit-articles', function () {\n        return true;\n    });\n\n    expect(app(Gate::class)->abilities())->toHaveKey('edit-articles');\n    expect($this->testUser->can('edit-articles'))->toBeTrue();\n});\n"
  },
  {
    "path": "tests/Integration/GateTest.php",
    "content": "<?php\n\nuse Illuminate\\Contracts\\Auth\\Access\\Gate;\nuse Spatie\\Permission\\Contracts\\Permission;\n\nit('can determine if a user does not have a permission', function () {\n    expect($this->testUser->can('edit-articles'))->toBeFalse();\n});\n\nit('allows other gate before callbacks to run if a user does not have a permission', function () {\n    expect($this->testUser->can('edit-articles'))->toBeFalse();\n\n    app(Gate::class)->before(function () {\n        // this Gate-before intercept overrides everything to true ... like a typical Super-Admin might use\n        return true;\n    });\n\n    expect($this->testUser->can('edit-articles'))->toBeTrue();\n});\n\nit('allows gate after callback to grant denied privileges', function () {\n    expect($this->testUser->can('edit-articles'))->toBeFalse();\n\n    app(Gate::class)->after(function ($user, $ability, $result) {\n        return true;\n    });\n\n    expect($this->testUser->can('edit-articles'))->toBeTrue();\n});\n\nit('can determine if a user has a direct permission', function () {\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->can('edit-articles'))->toBeTrue();\n    expect($this->testUser->can('non-existing-permission'))->toBeFalse();\n    expect($this->testUser->can('admin-permission'))->toBeFalse();\n});\n\nit('can determine if a user has a direct permission using enums', function () {\n    $enum = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::ViewArticles;\n\n    $permission = app(Permission::class)->findOrCreate($enum->value, 'web');\n\n    expect($this->testUser->can($enum->value))->toBeFalse();\n    expect($this->testUser->canAny([$enum->value, 'some other permission']))->toBeFalse();\n\n    $this->testUser->givePermissionTo($enum);\n\n    expect($this->testUser->hasPermissionTo($enum))->toBeTrue();\n    expect($this->testUser->can($enum->value))->toBeTrue();\n    expect($this->testUser->canAny([$enum->value, 'some other permission']))->toBeTrue();\n});\n\nit('can determine if a user has a permission through roles', function () {\n    $this->testUserRole->givePermissionTo($this->testUserPermission);\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeTrue();\n    expect($this->testUser->can('edit-articles'))->toBeTrue();\n    expect($this->testUser->can('non-existing-permission'))->toBeFalse();\n    expect($this->testUser->can('admin-permission'))->toBeFalse();\n});\n\nit('can determine if a user with a different guard has a permission when using roles', function () {\n    $this->testAdminRole->givePermissionTo($this->testAdminPermission);\n\n    $this->testAdmin->assignRole($this->testAdminRole);\n\n    expect($this->testAdmin->hasPermissionTo($this->testAdminPermission))->toBeTrue();\n    expect($this->testAdmin->can('admin-permission'))->toBeTrue();\n    expect($this->testAdmin->can('non-existing-permission'))->toBeFalse();\n    expect($this->testAdmin->can('edit-articles'))->toBeFalse();\n});\n"
  },
  {
    "path": "tests/Integration/MultipleGuardsTest.php",
    "content": "<?php\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Route;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Manager;\n\nbeforeEach(function () {\n    app('config')->set('auth.guards', [\n        'web' => ['driver' => 'session', 'provider' => 'users'],\n        'api' => ['driver' => 'token', 'provider' => 'users'],\n        'jwt' => ['driver' => 'token', 'provider' => 'users'],\n        'abc' => ['driver' => 'abc'],\n        'admin' => ['driver' => 'session', 'provider' => 'admins'],\n    ]);\n\n    Route::middleware('auth:api')->get('/check-api-guard-permission', function (Request $request) {\n        return ['status' => $request->user()->checkPermissionTo('use_api_guard')];\n    });\n});\n\nit('can give a permission to a model that is used by multiple guards', function () {\n    $this->testUser->givePermissionTo(app(Permission::class)::create([\n        'name' => 'do_this',\n        'guard_name' => 'web',\n    ]));\n\n    $this->testUser->givePermissionTo(app(Permission::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'api',\n    ]));\n\n    expect($this->testUser->checkPermissionTo('do_this', 'web'))->toBeTrue();\n    expect($this->testUser->checkPermissionTo('do_that', 'api'))->toBeTrue();\n    expect($this->testUser->checkPermissionTo('do_that', 'web'))->toBeFalse();\n});\n\nit('the gate can grant permission to a user by passing a guard name', function () {\n    $this->testUser->givePermissionTo(app(Permission::class)::create([\n        'name' => 'do_this',\n        'guard_name' => 'web',\n    ]));\n\n    $this->testUser->givePermissionTo(app(Permission::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'api',\n    ]));\n\n    expect($this->testUser->can('do_this', 'web'))->toBeTrue();\n    expect($this->testUser->can('do_that', 'api'))->toBeTrue();\n    expect($this->testUser->can('do_that', 'web'))->toBeFalse();\n\n    expect($this->testUser->cannot('do_that', 'web'))->toBeTrue();\n    expect($this->testUser->canAny(['do_this', 'do_that'], 'web'))->toBeTrue();\n\n    $this->testAdminRole->givePermissionTo($this->testAdminPermission);\n    $this->testAdmin->assignRole($this->testAdminRole);\n\n    expect($this->testAdmin->hasPermissionTo($this->testAdminPermission))->toBeTrue();\n    expect($this->testAdmin->can('admin-permission'))->toBeTrue();\n    expect($this->testAdmin->can('admin-permission', 'admin'))->toBeTrue();\n    expect($this->testAdmin->cannot('admin-permission', 'web'))->toBeTrue();\n\n    expect($this->testAdmin->cannot('non-existing-permission'))->toBeTrue();\n    expect($this->testAdmin->cannot('non-existing-permission', 'web'))->toBeTrue();\n    expect($this->testAdmin->cannot('non-existing-permission', 'admin'))->toBeTrue();\n    expect($this->testAdmin->cannot(['admin-permission', 'non-existing-permission'], 'web'))->toBeTrue();\n\n    expect($this->testAdmin->can('edit-articles', 'web'))->toBeFalse();\n    expect($this->testAdmin->can('edit-articles', 'admin'))->toBeFalse();\n\n    expect($this->testUser->cannot('edit-articles', 'admin'))->toBeTrue();\n    expect($this->testUser->cannot('admin-permission', 'admin'))->toBeTrue();\n    expect($this->testUser->cannot('admin-permission', 'web'))->toBeTrue();\n});\n\nit('can honour guardName function on model for overriding guard name property', function () {\n    $user = Manager::create(['email' => 'manager@test.com']);\n    $user->givePermissionTo(app(Permission::class)::create([\n        'name' => 'do_jwt',\n        'guard_name' => 'jwt',\n    ]));\n\n    // Manager test user has the guardName override method, which returns 'jwt'\n    expect($user->checkPermissionTo('do_jwt', 'jwt'))->toBeTrue();\n    expect($user->hasPermissionTo('do_jwt', 'jwt'))->toBeTrue();\n\n    // Manager test user has the $guard_name property set to 'web'\n    expect($user->checkPermissionTo('do_jwt', 'web'))->toBeFalse();\n});\n"
  },
  {
    "path": "tests/Integration/PermissionRegistrarTest.php",
    "content": "<?php\n\nuse Spatie\\Permission\\Contracts\\Permission as PermissionContract;\nuse Spatie\\Permission\\Contracts\\Role as RoleContract;\nuse Spatie\\Permission\\Models\\Permission as SpatiePermission;\nuse Spatie\\Permission\\Models\\Role as SpatieRole;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Permission as TestPermission;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Role as TestRole;\n\nit('can clear loaded permissions collection', function () {\n    $reflectedClass = new ReflectionClass(app(PermissionRegistrar::class));\n    $reflectedProperty = $reflectedClass->getProperty('permissions');\n    $reflectedProperty->setAccessible(true);\n\n    app(PermissionRegistrar::class)->getPermissions();\n\n    expect($reflectedProperty->getValue(app(PermissionRegistrar::class)))->not->toBeNull();\n\n    app(PermissionRegistrar::class)->clearPermissionsCollection();\n\n    expect($reflectedProperty->getValue(app(PermissionRegistrar::class)))->toBeNull();\n});\n\nit('can check uids', function () {\n    $uids = [\n        // UUIDs\n        '00000000-0000-0000-0000-000000000000',\n        '9be37b52-e1fa-4e86-b65f-cbfcbedde838',\n        'fc458041-fb21-4eea-a04b-b55c87a7224a',\n        '78144b52-a889-11ed-afa1-0242ac120002',\n        '78144f4e-a889-11ed-afa1-0242ac120002',\n        // GUIDs\n        '4b8590bb-90a2-4f38-8dc9-70e663a5b0e5',\n        'A98C5A1E-A742-4808-96FA-6F409E799937',\n        '1f01164a-98e9-4246-93ec-7941aefb1da6',\n        '91b73d20-89e6-46b0-b39b-632706cc3ed7',\n        '0df4a5b8-7c2e-484f-ad1d-787d1b83aacc',\n        // ULIDs\n        '01GRVB3DREB63KNN4G2QVV99DF',\n        '01GRVB3DRECY317SJCJ6DMTFCA',\n        '01GRVB3DREGGPBXNH1M24GX1DS',\n        '01GRVB3DRESRM2K9AVQSW1JCKA',\n        '01GRVB3DRES5CQ31PB24MP4CSV',\n    ];\n\n    $not_uids = [\n        '9be37b52-e1fa',\n        '9be37b52-e1fa-4e86',\n        '9be37b52-e1fa-4e86-b65f',\n        '01GRVB3DREB63KNN4G2',\n        'TEST STRING',\n        '00-00-00-00-00-00',\n        '91GRVB3DRES5CQ31PB24MP4CSV',\n    ];\n\n    foreach ($uids as $uid) {\n        expect(PermissionRegistrar::isUid($uid))->toBeTrue();\n    }\n\n    foreach ($not_uids as $not_uid) {\n        expect(PermissionRegistrar::isUid($not_uid))->toBeFalse();\n    }\n});\n\nit('can get permission class', function () {\n    expect(app(PermissionRegistrar::class)->getPermissionClass())->toBe(SpatiePermission::class);\n    expect(get_class(app(PermissionContract::class)))->toBe(SpatiePermission::class);\n});\n\nit('can change permission class', function () {\n    expect(config('permission.models.permission'))->toBe(SpatiePermission::class);\n    expect(app(PermissionRegistrar::class)->getPermissionClass())->toBe(SpatiePermission::class);\n    expect(get_class(app(PermissionContract::class)))->toBe(SpatiePermission::class);\n\n    app(PermissionRegistrar::class)->setPermissionClass(TestPermission::class);\n\n    expect(config('permission.models.permission'))->toBe(TestPermission::class);\n    expect(app(PermissionRegistrar::class)->getPermissionClass())->toBe(TestPermission::class);\n    expect(get_class(app(PermissionContract::class)))->toBe(TestPermission::class);\n});\n\nit('can get role class', function () {\n    expect(app(PermissionRegistrar::class)->getRoleClass())->toBe(SpatieRole::class);\n    expect(get_class(app(RoleContract::class)))->toBe(SpatieRole::class);\n});\n\nit('can change role class', function () {\n    expect(config('permission.models.role'))->toBe(SpatieRole::class);\n    expect(app(PermissionRegistrar::class)->getRoleClass())->toBe(SpatieRole::class);\n    expect(get_class(app(RoleContract::class)))->toBe(SpatieRole::class);\n\n    app(PermissionRegistrar::class)->setRoleClass(TestRole::class);\n\n    expect(config('permission.models.role'))->toBe(TestRole::class);\n    expect(app(PermissionRegistrar::class)->getRoleClass())->toBe(TestRole::class);\n    expect(get_class(app(RoleContract::class)))->toBe(TestRole::class);\n});\n\nit('can change team id', function () {\n    $team_id = '00000000-0000-0000-0000-000000000000';\n\n    app(PermissionRegistrar::class)->setPermissionsTeamId($team_id);\n\n    expect(app(PermissionRegistrar::class)->getPermissionsTeamId())->toBe($team_id);\n});\n"
  },
  {
    "path": "tests/Integration/PolicyTest.php",
    "content": "<?php\n\nuse Illuminate\\Contracts\\Auth\\Access\\Gate;\nuse Spatie\\Permission\\Tests\\TestSupport\\ContentPolicy;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Content;\n\nit('policy methods and before intercepts can allow and deny', function () {\n    $record1 = Content::create(['content' => 'special admin content']);\n    $record2 = Content::create(['content' => 'viewable', 'user_id' => $this->testUser->id]);\n\n    app(Gate::class)->policy(Content::class, ContentPolicy::class);\n\n    expect($this->testUser->can('view', $record1))->toBeFalse();\n    expect($this->testUser->can('update', $record1))->toBeFalse();\n\n    expect($this->testUser->can('update', $record2))->toBeTrue();\n\n    // test that the Admin cannot yet view 'special admin content', because doesn't have Role yet\n    expect($this->testAdmin->can('update', $record1))->toBeFalse();\n\n    $this->testAdmin->assignRole($this->testAdminRole);\n    // test that the Admin can view 'special admin content'\n    expect($this->testAdmin->can('update', $record1))->toBeTrue();\n    expect($this->testAdmin->can('update', $record2))->toBeTrue();\n});\n"
  },
  {
    "path": "tests/Integration/RouteTest.php",
    "content": "<?php\n\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum;\n\nit('test role function', function () {\n    $router = $this->getRouter();\n\n    $router->get('role-test', $this->getRouteResponse())\n        ->name('role.test')\n        ->role('superadmin');\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual(['role:superadmin']);\n});\n\nit('test permission function', function () {\n    $router = $this->getRouter();\n\n    $router->get('permission-test', $this->getRouteResponse())\n        ->name('permission.test')\n        ->permission(['edit articles', 'save articles']);\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual(['permission:edit articles|save articles']);\n});\n\nit('test role and permission function together', function () {\n    $router = $this->getRouter();\n\n    $router->get('role-permission-test', $this->getRouteResponse())\n        ->name('role-permission.test')\n        ->role('superadmin|admin')\n        ->permission('create user|edit user');\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual([\n        'role:superadmin|admin',\n        'permission:create user|edit user',\n    ]);\n});\n\nit('test role function with backed enum', function () {\n    $router = $this->getRouter();\n\n    $router->get('role-test.enum', $this->getRouteResponse())\n        ->name('role.test.enum')\n        ->role(TestRolePermissionsEnum::UserManager);\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual(['role:'.TestRolePermissionsEnum::UserManager->value]);\n});\n\nit('test permission function with backed enum', function () {\n    $router = $this->getRouter();\n\n    $router->get('permission-test.enum', $this->getRouteResponse())\n        ->name('permission.test.enum')\n        ->permission(TestRolePermissionsEnum::Writer);\n\n    $expected = ['permission:'.TestRolePermissionsEnum::Writer->value];\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual($expected);\n});\n\nit('test role and permission function together with backed enum', function () {\n    $router = $this->getRouter();\n\n    $router->get('roles-permissions-test.enum', $this->getRouteResponse())\n        ->name('roles-permissions.test.enum')\n        ->role([TestRolePermissionsEnum::UserManager, TestRolePermissionsEnum::Admin])\n        ->permission([TestRolePermissionsEnum::Writer, TestRolePermissionsEnum::Editor]);\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual([\n        'role:'.TestRolePermissionsEnum::UserManager->value.'|'.TestRolePermissionsEnum::Admin->value,\n        'permission:'.TestRolePermissionsEnum::Writer->value.'|'.TestRolePermissionsEnum::Editor->value,\n    ]);\n});\n\nit('test role or permission function', function () {\n    $router = $this->getRouter();\n\n    $router->get('role-or-permission-test', $this->getRouteResponse())\n        ->name('role-or-permission.test')\n        ->roleOrPermission('admin|edit articles');\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual(['role_or_permission:admin|edit articles']);\n});\n\nit('test role or permission function with array', function () {\n    $router = $this->getRouter();\n\n    $router->get('role-or-permission-array-test', $this->getRouteResponse())\n        ->name('role-or-permission-array.test')\n        ->roleOrPermission(['admin', 'edit articles']);\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual(['role_or_permission:admin|edit articles']);\n});\n\nit('test role or permission function with backed enum', function () {\n    $router = $this->getRouter();\n\n    $router->get('role-or-permission-test.enum', $this->getRouteResponse())\n        ->name('role-or-permission.test.enum')\n        ->roleOrPermission(TestRolePermissionsEnum::UserManager);\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual(['role_or_permission:'.TestRolePermissionsEnum::UserManager->value]);\n});\n\nit('test role or permission function with backed enum array', function () {\n    $router = $this->getRouter();\n\n    $router->get('role-or-permission-array-test.enum', $this->getRouteResponse())\n        ->name('role-or-permission-array.test.enum')\n        ->roleOrPermission([TestRolePermissionsEnum::UserManager, TestRolePermissionsEnum::EditArticles]); // @phpstan-ignore-line\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual(\n        ['role_or_permission:'.TestRolePermissionsEnum::UserManager->value.'|'.TestRolePermissionsEnum::EditArticles->value] // @phpstan-ignore-line\n    );\n});\n"
  },
  {
    "path": "tests/Integration/WildcardRouteTest.php",
    "content": "<?php\n\nit('test permission function', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $router = $this->getRouter();\n\n    $router->get('permission-test', $this->getRouteResponse())\n        ->name('permission.test')\n        ->permission(['articles.edit', 'articles.save']);\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual(['permission:articles.edit|articles.save']);\n});\n\nit('test role and permission function together', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $router = $this->getRouter();\n\n    $router->get('role-permission-test', $this->getRouteResponse())\n        ->name('role-permission.test')\n        ->role('superadmin|admin')\n        ->permission('user.create|user.edit');\n\n    expect($this->getLastRouteMiddlewareFromRouter($router))->toEqual([\n        'role:superadmin|admin',\n        'permission:user.create|user.edit',\n    ]);\n});\n"
  },
  {
    "path": "tests/Middleware/PermissionMiddlewareTest.php",
    "content": "<?php\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Gate;\nuse Laravel\\Passport\\Passport;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Exceptions\\UnauthorizedException;\nuse Spatie\\Permission\\Middleware\\PermissionMiddleware;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\UserWithoutHasRoles;\n\nbeforeEach(function () {\n    $this->setUpPassport();\n    $this->permissionMiddleware = new PermissionMiddleware;\n});\n\nit('a guest cannot access a route protected by the permission middleware', function () {\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles'))->toEqual(403);\n});\n\nit('a user cannot access a route protected by the permission middleware of a different guard', function () {\n    // These permissions are created fresh here in reverse order of guard being applied, so they are not \"found first\" in the db lookup when matching\n    app(Permission::class)->create(['name' => 'admin-permission2', 'guard_name' => 'web']);\n    $p1 = app(Permission::class)->create(['name' => 'admin-permission2', 'guard_name' => 'admin']);\n    app(Permission::class)->create(['name' => 'edit-articles2', 'guard_name' => 'admin']);\n    $p2 = app(Permission::class)->create(['name' => 'edit-articles2', 'guard_name' => 'web']);\n\n    Auth::guard('admin')->login($this->testAdmin);\n\n    $this->testAdmin->givePermissionTo($p1);\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'admin-permission2', 'admin'))->toEqual(200);\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles2', 'admin'))->toEqual(403);\n\n    Auth::login($this->testUser);\n\n    $this->testUser->givePermissionTo($p2);\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles2', 'web'))->toEqual(200);\n    expect($this->runMiddleware($this->permissionMiddleware, 'admin-permission2', 'web'))->toEqual(403);\n});\n\nit('a client cannot access a route protected by the permission middleware of a different guard', function () {\n    // These permissions are created fresh here in reverse order of guard being applied, so they are not \"found first\" in the db lookup when matching\n    app(Permission::class)->create(['name' => 'admin-permission2', 'guard_name' => 'web']);\n    $p1 = app(Permission::class)->create(['name' => 'admin-permission2', 'guard_name' => 'api']);\n\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->givePermissionTo($p1);\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'admin-permission2', 'api', true))->toEqual(200);\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles2', 'web', true))->toEqual(403);\n});\n\nit('a super admin user can access a route protected by permission middleware', function () {\n    Auth::login($this->testUser);\n\n    Gate::before(function ($user, $ability) {\n        return $user->getKey() === $this->testUser->getKey() ? true : null;\n    });\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles'))->toEqual(200);\n});\n\nit('a user can access a route protected by permission middleware if have this permission', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles'))->toEqual(200);\n});\n\nit('a client can access a route protected by permission middleware if have this permission', function () {\n    Passport::actingAsClient($this->testClient, ['*'], 'api');\n\n    $this->testClient->givePermissionTo('edit-posts');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-posts', null, true))->toEqual(200);\n});\n\nit('a user can access a route protected by this permission middleware if have one of the permissions', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-news|edit-articles'))->toEqual(200);\n    expect($this->runMiddleware($this->permissionMiddleware, ['edit-news', 'edit-articles']))->toEqual(200);\n});\n\nit('a client can access a route protected by this permission middleware if have one of the permissions', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->givePermissionTo('edit-posts');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-news|edit-posts', null, true))->toEqual(200);\n    expect($this->runMiddleware($this->permissionMiddleware, ['edit-news', 'edit-posts'], null, true))->toEqual(200);\n});\n\nit('a user cannot access a route protected by the permission middleware if have not has roles trait', function () {\n    $userWithoutHasRoles = UserWithoutHasRoles::create(['email' => 'test_not_has_roles@user.com']);\n\n    Auth::login($userWithoutHasRoles);\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-news'))->toEqual(403);\n});\n\nit('a user cannot access a route protected by the permission middleware if have a different permission', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-news'))->toEqual(403);\n});\n\nit('a client cannot access a route protected by the permission middleware if have a different permission', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->givePermissionTo('edit-posts');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-news', null, true))->toEqual(403);\n});\n\nit('a user cannot access a route protected by permission middleware if have not permissions', function () {\n    Auth::login($this->testUser);\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles|edit-news'))->toEqual(403);\n});\n\nit('a client cannot access a route protected by permission middleware if have not permissions', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles|edit-posts', null, true))->toEqual(403);\n});\n\nit('a user can access a route protected by permission middleware if has permission via role', function () {\n    Auth::login($this->testUser);\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles'))->toEqual(403);\n\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $this->testUser->assignRole('testRole');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles'))->toEqual(200);\n});\n\nit('a client can access a route protected by permission middleware if has permission via role', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles', null, true))->toEqual(403);\n\n    $this->testClientRole->givePermissionTo('edit-posts');\n    $this->testClient->assignRole('clientRole');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-posts', null, true))->toEqual(200);\n});\n\nit('the required permissions can be fetched from the exception', function () {\n    Auth::login($this->testUser);\n\n    $message = null;\n    $requiredPermissions = [];\n\n    try {\n        $this->permissionMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'some-permission');\n    } catch (UnauthorizedException $e) {\n        $message = $e->getMessage();\n        $requiredPermissions = $e->getRequiredPermissions();\n    }\n\n    expect($message)->toEqual('User does not have the right permissions.');\n    expect($requiredPermissions)->toEqual(['some-permission']);\n});\n\nit('the required permissions can be displayed in the exception', function () {\n    Auth::login($this->testUser);\n    Config::set(['permission.display_permission_in_exception' => true]);\n\n    $message = null;\n\n    try {\n        $this->permissionMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'some-permission');\n    } catch (UnauthorizedException $e) {\n        $message = $e->getMessage();\n    }\n\n    expect($message)->toEndWith('Necessary permissions are some-permission');\n});\n\nit('use not existing custom guard in permission', function () {\n    $class = null;\n\n    try {\n        $this->permissionMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'edit-articles', 'xxx');\n    } catch (InvalidArgumentException $e) {\n        $class = get_class($e);\n    }\n\n    expect($class)->toEqual(InvalidArgumentException::class);\n});\n\nit('user can not access permission with guard admin while login using default guard', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-articles', 'admin'))->toEqual(403);\n});\n\nit('client can not access permission with guard admin while login using default guard', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->givePermissionTo('edit-posts');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'edit-posts', 'admin', true))->toEqual(403);\n});\n\nit('user can access permission with guard admin while login using admin guard', function () {\n    Auth::guard('admin')->login($this->testAdmin);\n\n    $this->testAdmin->givePermissionTo('admin-permission');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'admin-permission', 'admin'))->toEqual(200);\n});\n\nit('the middleware can be created with static using method', function () {\n    expect(PermissionMiddleware::using('edit-articles'))\n        ->toBe('Spatie\\Permission\\Middleware\\PermissionMiddleware:edit-articles');\n\n    expect(PermissionMiddleware::using('edit-articles', 'my-guard'))\n        ->toEqual('Spatie\\Permission\\Middleware\\PermissionMiddleware:edit-articles,my-guard');\n\n    expect(PermissionMiddleware::using(['edit-articles', 'edit-news']))\n        ->toEqual('Spatie\\Permission\\Middleware\\PermissionMiddleware:edit-articles|edit-news');\n});\n\nit('the middleware can handle enum based permissions with static using method', function () {\n    expect(PermissionMiddleware::using(TestModels\\TestRolePermissionsEnum::ViewArticles))\n        ->toBe('Spatie\\Permission\\Middleware\\PermissionMiddleware:view articles');\n\n    expect(PermissionMiddleware::using(TestModels\\TestRolePermissionsEnum::ViewArticles, 'my-guard'))\n        ->toEqual('Spatie\\Permission\\Middleware\\PermissionMiddleware:view articles,my-guard');\n\n    expect(PermissionMiddleware::using([TestModels\\TestRolePermissionsEnum::ViewArticles, TestModels\\TestRolePermissionsEnum::EditArticles]))\n        ->toEqual('Spatie\\Permission\\Middleware\\PermissionMiddleware:view articles|edit articles');\n});\n\nit('the middleware can handle enum based permissions with handle method', function () {\n    app(Permission::class)->create(['name' => TestModels\\TestRolePermissionsEnum::ViewArticles->value]);\n    app(Permission::class)->create(['name' => TestModels\\TestRolePermissionsEnum::EditArticles->value]);\n\n    Auth::login($this->testUser);\n    $this->testUser->givePermissionTo(TestModels\\TestRolePermissionsEnum::ViewArticles);\n\n    expect($this->runMiddleware($this->permissionMiddleware, TestModels\\TestRolePermissionsEnum::ViewArticles))\n        ->toEqual(200);\n\n    $this->testUser->givePermissionTo(TestModels\\TestRolePermissionsEnum::EditArticles);\n\n    expect($this->runMiddleware($this->permissionMiddleware, [TestModels\\TestRolePermissionsEnum::ViewArticles, TestModels\\TestRolePermissionsEnum::EditArticles]))\n        ->toEqual(200);\n});\n"
  },
  {
    "path": "tests/Middleware/RoleMiddlewareTest.php",
    "content": "<?php\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Support\\Facades\\Config;\nuse Laravel\\Passport\\Passport;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Exceptions\\UnauthorizedException;\nuse Spatie\\Permission\\Middleware\\RoleMiddleware;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\UserWithoutHasRoles;\n\nbeforeEach(function () {\n    $this->setUpPassport();\n    $this->roleMiddleware = new RoleMiddleware;\n});\n\nit('a guest cannot access a route protected by rolemiddleware', function () {\n    expect($this->runMiddleware($this->roleMiddleware, 'testRole'))->toEqual(403);\n});\n\nit('a user cannot access a route protected by role middleware of another guard', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testAdminRole'))->toEqual(403);\n});\n\nit('a client cannot access a route protected by role middleware of another guard', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->assignRole('clientRole');\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testAdminRole', null, true))->toEqual(403);\n});\n\nit('a user can access a route protected by role middleware if have this role', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testRole'))->toEqual(200);\n});\n\nit('a client can access a route protected by role middleware if have this role', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->assignRole('clientRole');\n\n    expect($this->runMiddleware($this->roleMiddleware, 'clientRole', null, true))->toEqual(200);\n});\n\nit('a user can access a route protected by this role middleware if have one of the roles', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testRole|testRole2'))->toEqual(200);\n    expect($this->runMiddleware($this->roleMiddleware, ['testRole2', 'testRole']))->toEqual(200);\n});\n\nit('a client can access a route protected by this role middleware if have one of the roles', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->assignRole('clientRole');\n\n    expect($this->runMiddleware($this->roleMiddleware, 'clientRole|testRole2', null, true))->toEqual(200);\n    expect($this->runMiddleware($this->roleMiddleware, ['testRole2', 'clientRole'], null, true))->toEqual(200);\n});\n\nit('a user cannot access a route protected by the role middleware if have not has roles trait', function () {\n    $userWithoutHasRoles = UserWithoutHasRoles::create(['email' => 'test_not_has_roles@user.com']);\n\n    Auth::login($userWithoutHasRoles);\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testRole'))->toEqual(403);\n});\n\nit('a user cannot access a route protected by the role middleware if have a different role', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->assignRole(['testRole']);\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testRole2'))->toEqual(403);\n});\n\nit('a client cannot access a route protected by the role middleware if have a different role', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->assignRole(['clientRole']);\n\n    expect($this->runMiddleware($this->roleMiddleware, 'clientRole2', null, true))->toEqual(403);\n});\n\nit('a user cannot access a route protected by role middleware if have not roles', function () {\n    Auth::login($this->testUser);\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testRole|testRole2'))->toEqual(403);\n});\n\nit('a client cannot access a route protected by role middleware if have not roles', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testRole|testRole2', null, true))->toEqual(403);\n});\n\nit('a user cannot access a route protected by role middleware if role is undefined', function () {\n    Auth::login($this->testUser);\n\n    expect($this->runMiddleware($this->roleMiddleware, ''))->toEqual(403);\n});\n\nit('a client cannot access a route protected by role middleware if role is undefined', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    expect($this->runMiddleware($this->roleMiddleware, '', null, true))->toEqual(403);\n});\n\nit('the required roles can be fetched from the exception', function () {\n    Auth::login($this->testUser);\n\n    $message = null;\n    $requiredRoles = [];\n\n    try {\n        $this->roleMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'some-role');\n    } catch (UnauthorizedException $e) {\n        $message = $e->getMessage();\n        $requiredRoles = $e->getRequiredRoles();\n    }\n\n    expect($message)->toEqual('User does not have the right roles.');\n    expect($requiredRoles)->toEqual(['some-role']);\n});\n\nit('the required roles can be displayed in the exception', function () {\n    Auth::login($this->testUser);\n    Config::set(['permission.display_role_in_exception' => true]);\n\n    $message = null;\n\n    try {\n        $this->roleMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'some-role');\n    } catch (UnauthorizedException $e) {\n        $message = $e->getMessage();\n    }\n\n    expect($message)->toEndWith('Necessary roles are some-role');\n});\n\nit('use not existing custom guard in role', function () {\n    $class = null;\n\n    try {\n        $this->roleMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'testRole', 'xxx');\n    } catch (InvalidArgumentException $e) {\n        $class = get_class($e);\n    }\n\n    expect($class)->toEqual(InvalidArgumentException::class);\n});\n\nit('user can not access role with guard admin while login using default guard', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testRole', 'admin'))->toEqual(403);\n});\n\nit('client can not access role with guard admin while login using default guard', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->assignRole('clientRole');\n\n    expect($this->runMiddleware($this->roleMiddleware, 'clientRole', 'admin', true))->toEqual(403);\n});\n\nit('user can access role with guard admin while login using admin guard', function () {\n    Auth::guard('admin')->login($this->testAdmin);\n\n    $this->testAdmin->assignRole('testAdminRole');\n\n    expect($this->runMiddleware($this->roleMiddleware, 'testAdminRole', 'admin'))->toEqual(200);\n});\n\nit('the middleware can be created with static using method', function () {\n    expect(RoleMiddleware::using('testAdminRole'))\n        ->toBe('Spatie\\Permission\\Middleware\\RoleMiddleware:testAdminRole');\n\n    expect(RoleMiddleware::using('testAdminRole', 'my-guard'))\n        ->toEqual('Spatie\\Permission\\Middleware\\RoleMiddleware:testAdminRole,my-guard');\n\n    expect(RoleMiddleware::using(['testAdminRole', 'anotherRole']))\n        ->toEqual('Spatie\\Permission\\Middleware\\RoleMiddleware:testAdminRole|anotherRole');\n});\n\nit('the middleware can handle enum based roles with static using method', function () {\n    expect(RoleMiddleware::using(TestModels\\TestRolePermissionsEnum::Writer))\n        ->toBe('Spatie\\Permission\\Middleware\\RoleMiddleware:writer');\n\n    expect(RoleMiddleware::using(TestModels\\TestRolePermissionsEnum::Writer, 'my-guard'))\n        ->toEqual('Spatie\\Permission\\Middleware\\RoleMiddleware:writer,my-guard');\n\n    expect(RoleMiddleware::using([TestModels\\TestRolePermissionsEnum::Writer, TestModels\\TestRolePermissionsEnum::Editor]))\n        ->toEqual('Spatie\\Permission\\Middleware\\RoleMiddleware:writer|editor');\n});\n\nit('the middleware can handle enum based roles with handle method', function () {\n    app(Role::class)->create(['name' => TestModels\\TestRolePermissionsEnum::Writer->value]);\n    app(Role::class)->create(['name' => TestModels\\TestRolePermissionsEnum::Editor->value]);\n\n    Auth::login($this->testUser);\n    $this->testUser->assignRole(TestModels\\TestRolePermissionsEnum::Writer);\n\n    expect($this->runMiddleware($this->roleMiddleware, TestModels\\TestRolePermissionsEnum::Writer))\n        ->toEqual(200);\n\n    $this->testUser->assignRole(TestModels\\TestRolePermissionsEnum::Editor);\n\n    expect($this->runMiddleware($this->roleMiddleware, [TestModels\\TestRolePermissionsEnum::Writer, TestModels\\TestRolePermissionsEnum::Editor]))\n        ->toEqual(200);\n});\n"
  },
  {
    "path": "tests/Middleware/RoleOrPermissionMiddlewareTest.php",
    "content": "<?php\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Gate;\nuse Laravel\\Passport\\Passport;\nuse Spatie\\Permission\\Exceptions\\UnauthorizedException;\nuse Spatie\\Permission\\Middleware\\RoleOrPermissionMiddleware;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\UserWithoutHasRoles;\n\nbeforeEach(function () {\n    $this->setUpPassport();\n    $this->roleOrPermissionMiddleware = new RoleOrPermissionMiddleware;\n});\n\nit('a guest cannot access a route protected by the role or permission middleware', function () {\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole'))->toEqual(403);\n});\n\nit('a user can access a route protected by permission or role middleware if has this permission or role', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->assignRole('testRole');\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole|edit-news|edit-articles'))->toEqual(200);\n\n    $this->testUser->removeRole('testRole');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole|edit-articles'))->toEqual(200);\n\n    $this->testUser->revokePermissionTo('edit-articles');\n    $this->testUser->assignRole('testRole');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole|edit-articles'))->toEqual(200);\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, ['testRole', 'edit-articles']))->toEqual(200);\n});\n\nit('a client can access a route protected by permission or role middleware if has this permission or role', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->assignRole('clientRole');\n    $this->testClient->givePermissionTo('edit-posts');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'clientRole|edit-news|edit-posts', null, true))->toEqual(200);\n\n    $this->testClient->removeRole('clientRole');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'clientRole|edit-posts', null, true))->toEqual(200);\n\n    $this->testClient->revokePermissionTo('edit-posts');\n    $this->testClient->assignRole('clientRole');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'clientRole|edit-posts', null, true))->toEqual(200);\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, ['clientRole', 'edit-posts'], null, true))->toEqual(200);\n});\n\nit('a super admin user can access a route protected by permission or role middleware', function () {\n    Auth::login($this->testUser);\n\n    Gate::before(function ($user, $ability) {\n        return $user->getKey() === $this->testUser->getKey() ? true : null;\n    });\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole|edit-articles'))->toEqual(200);\n});\n\nit('a user can not access a route protected by permission or role middleware if have not has roles trait', function () {\n    $userWithoutHasRoles = UserWithoutHasRoles::create(['email' => 'test_not_has_roles@user.com']);\n\n    Auth::login($userWithoutHasRoles);\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole|edit-articles'))->toEqual(403);\n});\n\nit('a user can not access a route protected by permission or role middleware if have not this permission and role', function () {\n    Auth::login($this->testUser);\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole|edit-articles'))->toEqual(403);\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'missingRole|missingPermission'))->toEqual(403);\n});\n\nit('a client can not access a route protected by permission or role middleware if have not this permission and role', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'clientRole|edit-posts', null, true))->toEqual(403);\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'missingRole|missingPermission', null, true))->toEqual(403);\n});\n\nit('use not existing custom guard in role or permission', function () {\n    $class = null;\n\n    try {\n        $this->roleOrPermissionMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'testRole', 'xxx');\n    } catch (InvalidArgumentException $e) {\n        $class = get_class($e);\n    }\n\n    expect($class)->toEqual(InvalidArgumentException::class);\n});\n\nit('user can not access permission or role with guard admin while login using default guard', function () {\n    Auth::login($this->testUser);\n\n    $this->testUser->assignRole('testRole');\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'edit-articles|testRole', 'admin'))->toEqual(403);\n});\n\nit('client can not access permission or role with guard admin while login using default guard', function () {\n    Passport::actingAsClient($this->testClient, ['*']);\n\n    $this->testClient->assignRole('clientRole');\n    $this->testClient->givePermissionTo('edit-posts');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'edit-posts|clientRole', 'admin', true))->toEqual(403);\n});\n\nit('user can access permission or role with guard admin while login using admin guard', function () {\n    Auth::guard('admin')->login($this->testAdmin);\n\n    $this->testAdmin->assignRole('testAdminRole');\n    $this->testAdmin->givePermissionTo('admin-permission');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'admin-permission|testAdminRole', 'admin'))->toEqual(200);\n});\n\nit('the required permissions or roles can be fetched from the exception', function () {\n    Auth::login($this->testUser);\n\n    $message = null;\n    $requiredRolesOrPermissions = [];\n\n    try {\n        $this->roleOrPermissionMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'some-permission|some-role');\n    } catch (UnauthorizedException $e) {\n        $message = $e->getMessage();\n        $requiredRolesOrPermissions = $e->getRequiredPermissions();\n    }\n\n    expect($message)->toEqual('User does not have any of the necessary access rights.');\n    expect($requiredRolesOrPermissions)->toEqual(['some-permission', 'some-role']);\n});\n\nit('the required permissions or roles can be displayed in the exception', function () {\n    Auth::login($this->testUser);\n    Config::set(['permission.display_permission_in_exception' => true]);\n    Config::set(['permission.display_role_in_exception' => true]);\n\n    $message = null;\n\n    try {\n        $this->roleOrPermissionMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'some-permission|some-role');\n    } catch (UnauthorizedException $e) {\n        $message = $e->getMessage();\n    }\n\n    expect($message)->toEndWith('Necessary roles or permissions are some-permission, some-role');\n});\n\nit('the middleware can be created with static using method', function () {\n    expect(RoleOrPermissionMiddleware::using('edit-articles'))\n        ->toBe('Spatie\\Permission\\Middleware\\RoleOrPermissionMiddleware:edit-articles');\n\n    expect(RoleOrPermissionMiddleware::using('edit-articles', 'my-guard'))\n        ->toEqual('Spatie\\Permission\\Middleware\\RoleOrPermissionMiddleware:edit-articles,my-guard');\n\n    expect(RoleOrPermissionMiddleware::using(['edit-articles', 'testAdminRole']))\n        ->toEqual('Spatie\\Permission\\Middleware\\RoleOrPermissionMiddleware:edit-articles|testAdminRole');\n});\n"
  },
  {
    "path": "tests/Middleware/WildcardMiddlewareTest.php",
    "content": "<?php\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Spatie\\Permission\\Exceptions\\UnauthorizedException;\nuse Spatie\\Permission\\Middleware\\PermissionMiddleware;\nuse Spatie\\Permission\\Middleware\\RoleMiddleware;\nuse Spatie\\Permission\\Middleware\\RoleOrPermissionMiddleware;\nuse Spatie\\Permission\\Models\\Permission;\n\nbeforeEach(function () {\n    $this->roleMiddleware = new RoleMiddleware;\n    $this->permissionMiddleware = new PermissionMiddleware;\n    $this->roleOrPermissionMiddleware = new RoleOrPermissionMiddleware;\n\n    app('config')->set('permission.enable_wildcard_permission', true);\n});\n\nit('does not allow a guest to access a route protected by the permission middleware', function () {\n    expect($this->runMiddleware($this->permissionMiddleware, 'articles.edit'))->toBe(403);\n});\n\nit('allows a user to access a route protected by permission middleware if they have this permission', function () {\n    Auth::login($this->testUser);\n\n    Permission::create(['name' => 'articles']);\n\n    $this->testUser->givePermissionTo('articles');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'articles.edit'))->toBe(200);\n});\n\nit('allows a user to access a route protected by this permission middleware if they have one of the permissions', function () {\n    Auth::login($this->testUser);\n\n    Permission::create(['name' => 'articles.*.test']);\n\n    $this->testUser->givePermissionTo('articles.*.test');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'news.edit|articles.create.test'))->toBe(200);\n    expect($this->runMiddleware($this->permissionMiddleware, ['news.edit', 'articles.create.test']))->toBe(200);\n});\n\nit('does not allow a user to access a route protected by the permission middleware if they have a different permission', function () {\n    Auth::login($this->testUser);\n\n    Permission::create(['name' => 'articles.*']);\n\n    $this->testUser->givePermissionTo('articles.*');\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'news.edit'))->toBe(403);\n});\n\nit('does not allow a user to access a route protected by permission middleware if they have no permissions', function () {\n    Auth::login($this->testUser);\n\n    expect($this->runMiddleware($this->permissionMiddleware, 'articles.edit|news.edit'))->toBe(403);\n});\n\nit('allows a user to access a route protected by permission or role middleware if they have this permission or role', function () {\n    Auth::login($this->testUser);\n\n    Permission::create(['name' => 'articles.*']);\n\n    $this->testUser->assignRole('testRole');\n    $this->testUser->givePermissionTo('articles.*');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole|news.edit|articles.create'))->toBe(200);\n\n    $this->testUser->removeRole('testRole');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole|articles.edit'))->toBe(200);\n\n    $this->testUser->revokePermissionTo('articles.*');\n    $this->testUser->assignRole('testRole');\n\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, 'testRole|articles.edit'))->toBe(200);\n    expect($this->runMiddleware($this->roleOrPermissionMiddleware, ['testRole', 'articles.edit']))->toBe(200);\n});\n\nit('can fetch the required permissions from the exception', function () {\n    Auth::login($this->testUser);\n\n    $requiredPermissions = [];\n\n    try {\n        $this->permissionMiddleware->handle(new Request, function () {\n            return (new Response)->setContent('<html></html>');\n        }, 'permission.some');\n    } catch (UnauthorizedException $e) {\n        $requiredPermissions = $e->getRequiredPermissions();\n    }\n\n    expect($requiredPermissions)->toBe(['permission.some']);\n});\n"
  },
  {
    "path": "tests/Models/PermissionTest.php",
    "content": "<?php\n\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Exceptions\\PermissionAlreadyExists;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nit('gets user models using with', function () {\n    $this->testUser->givePermissionTo($this->testUserPermission);\n\n    $permission = app(Permission::class)::with('users')\n        ->where($this->testUserPermission->getKeyName(), $this->testUserPermission->getKey())\n        ->first();\n\n    expect($this->testUserPermission->getKey())->toEqual($permission->getKey());\n    expect($permission->users)->toHaveCount(1);\n    expect($this->testUser->id)->toEqual($permission->users[0]->id);\n});\n\nit('throws an exception when the permission already exists', function () {\n    app(Permission::class)->create(['name' => 'test-permission']);\n\n    expect(fn () => app(Permission::class)->create(['name' => 'test-permission']))\n        ->toThrow(PermissionAlreadyExists::class);\n});\n\nit('belongs to a guard', function () {\n    $permission = app(Permission::class)->create(['name' => 'can-edit', 'guard_name' => 'admin']);\n\n    expect($permission->guard_name)->toEqual('admin');\n});\n\nit('belongs to the default guard by default', function () {\n    expect($this->testUserPermission->guard_name)->toEqual(\n        $this->app['config']->get('auth.defaults.guard')\n    );\n});\n\nit('has user models of the right class', function () {\n    $this->testAdmin->givePermissionTo($this->testAdminPermission);\n\n    $this->testUser->givePermissionTo($this->testUserPermission);\n\n    expect($this->testUserPermission->users)->toHaveCount(1);\n    expect($this->testUserPermission->users->first()->is($this->testUser))->toBeTrue();\n    expect($this->testUserPermission->users->first())->toBeInstanceOf(User::class);\n});\n\nit('is retrievable by id', function () {\n    $permission_by_id = app(Permission::class)->findById($this->testUserPermission->id);\n\n    expect($permission_by_id->id)->toEqual($this->testUserPermission->id);\n});\n\nit('can delete hydrated permissions', function () {\n    $this->reloadPermissions();\n\n    $permission = app(Permission::class)->findByName($this->testUserPermission->name);\n    $permission->delete();\n\n    expect(app(Permission::class)->where($this->testUserPermission->getKeyName(), $this->testUserPermission->getKey())->get())->toHaveCount(0);\n});\n\nit('does not treat string \"0\" as empty when giving permission', function () {\n    app(Permission::class)->create(['name' => '0']);\n\n    $this->testUser->givePermissionTo('0');\n\n    expect($this->testUser->hasPermissionTo('0'))->toBeTrue();\n});\n"
  },
  {
    "path": "tests/Models/RoleTest.php",
    "content": "<?php\n\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Exceptions\\GuardDoesNotMatch;\nuse Spatie\\Permission\\Exceptions\\PermissionDoesNotExist;\nuse Spatie\\Permission\\Exceptions\\RoleAlreadyExists;\nuse Spatie\\Permission\\Exceptions\\RoleDoesNotExist;\nuse Spatie\\Permission\\Models\\Permission;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Admin;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\RuntimeRole;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nbeforeEach(function () {\n    Permission::create(['name' => 'other-permission']);\n    Permission::create(['name' => 'wrong-guard-permission', 'guard_name' => 'admin']);\n});\n\nit('get user models using with', function () {\n    $this->testUser->assignRole($this->testUserRole);\n\n    $role = app(Role::class)::with('users')\n        ->where($this->testUserRole->getKeyName(), $this->testUserRole->getKey())->first();\n\n    expect($role->getKey())->toEqual($this->testUserRole->getKey());\n    expect($role->users)->toHaveCount(1);\n    expect($role->users[0]->id)->toEqual($this->testUser->id);\n});\n\nit('has user models of the right class', function () {\n    $this->testAdmin->assignRole($this->testAdminRole);\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUserRole->users)->toHaveCount(1);\n    expect($this->testUserRole->users->first()->is($this->testUser))->toBeTrue();\n    expect($this->testUserRole->users->first())->toBeInstanceOf(User::class);\n\n    expect($this->testAdminRole->users)->toHaveCount(1);\n    expect($this->testAdminRole->users->first()->is($this->testAdmin))->toBeTrue();\n    expect($this->testAdminRole->users->first())->toBeInstanceOf(Admin::class);\n});\n\nit('throws an exception when the role already exists', function () {\n    app(Role::class)->create(['name' => 'test-role']);\n\n    expect(fn () => app(Role::class)->create(['name' => 'test-role']))\n        ->toThrow(RoleAlreadyExists::class);\n});\n\nit('can be given a permission', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    expect($this->testUserRole->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n\nit('throws an exception when given a permission that does not exist', function () {\n    expect(fn () => $this->testUserRole->givePermissionTo('create-evil-empire'))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when given a permission that belongs to another guard', function () {\n    expect(fn () => $this->testUserRole->givePermissionTo('admin-permission'))\n        ->toThrow(PermissionDoesNotExist::class);\n\n    expect(fn () => $this->testUserRole->givePermissionTo($this->testAdminPermission))\n        ->toThrow(GuardDoesNotMatch::class);\n});\n\nit('can be given multiple permissions using an array', function () {\n    $this->testUserRole->givePermissionTo(['edit-articles', 'edit-news']);\n\n    expect($this->testUserRole->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($this->testUserRole->hasPermissionTo('edit-news'))->toBeTrue();\n});\n\nit('can be given multiple permissions using multiple arguments', function () {\n    $this->testUserRole->givePermissionTo('edit-articles', 'edit-news');\n\n    expect($this->testUserRole->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($this->testUserRole->hasPermissionTo('edit-news'))->toBeTrue();\n});\n\nit('can sync permissions', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    $this->testUserRole->syncPermissions('edit-news');\n\n    expect($this->testUserRole->hasPermissionTo('edit-articles'))->toBeFalse();\n    expect($this->testUserRole->hasPermissionTo('edit-news'))->toBeTrue();\n});\n\nit('throws an exception when syncing permissions that do not exist', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    expect(fn () => $this->testUserRole->syncPermissions('permission-does-not-exist'))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when syncing permissions that belong to a different guard', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    expect(fn () => $this->testUserRole->syncPermissions('admin-permission'))\n        ->toThrow(PermissionDoesNotExist::class);\n\n    expect(fn () => $this->testUserRole->syncPermissions($this->testAdminPermission))\n        ->toThrow(GuardDoesNotMatch::class);\n});\n\nit('will remove all permissions when passing an empty array to sync permissions', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $this->testUserRole->givePermissionTo('edit-news');\n\n    $this->testUserRole->syncPermissions([]);\n\n    expect($this->testUserRole->hasPermissionTo('edit-articles'))->toBeFalse();\n    expect($this->testUserRole->hasPermissionTo('edit-news'))->toBeFalse();\n});\n\ntest('sync permission error does not detach permissions', function () {\n    $this->testUserRole->givePermissionTo('edit-news');\n\n    expect(fn () => $this->testUserRole->syncPermissions('edit-articles', 'permission-that-does-not-exist'))\n        ->toThrow(PermissionDoesNotExist::class);\n\n    expect($this->testUserRole->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('can revoke a permission', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    expect($this->testUserRole->hasPermissionTo('edit-articles'))->toBeTrue();\n\n    $this->testUserRole->revokePermissionTo('edit-articles');\n\n    $this->testUserRole = $this->testUserRole->fresh();\n\n    expect($this->testUserRole->hasPermissionTo('edit-articles'))->toBeFalse();\n});\n\nit('can be given a permission using objects', function () {\n    $this->testUserRole->givePermissionTo($this->testUserPermission);\n\n    expect($this->testUserRole->hasPermissionTo($this->testUserPermission))->toBeTrue();\n});\n\nit('returns false if it does not have the permission', function () {\n    expect($this->testUserRole->hasPermissionTo('other-permission'))->toBeFalse();\n});\n\nit('throws an exception if the permission does not exist', function () {\n    expect(fn () => $this->testUserRole->hasPermissionTo('doesnt-exist'))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('returns false if it does not have a permission object', function () {\n    $permission = app(Permission::class)->findByName('other-permission');\n\n    expect($this->testUserRole->hasPermissionTo($permission))->toBeFalse();\n});\n\nit('creates permission object with findOrCreate if it does not have a permission object', function () {\n    $permission = app(Permission::class)->findOrCreate('another-permission');\n\n    expect($this->testUserRole->hasPermissionTo($permission))->toBeFalse();\n\n    $this->testUserRole->givePermissionTo($permission);\n\n    $this->testUserRole = $this->testUserRole->fresh();\n\n    expect($this->testUserRole->hasPermissionTo('another-permission'))->toBeTrue();\n});\n\nit('creates a role with findOrCreate if the named role does not exist', function () {\n    expect(fn () => app(Role::class)->findByName('non-existing-role'))\n        ->toThrow(RoleDoesNotExist::class);\n\n    $role2 = app(Role::class)->findOrCreate('yet-another-role');\n\n    expect($role2)->toBeInstanceOf(app(Role::class)::class);\n});\n\nit('throws an exception when a permission of the wrong guard is passed in', function () {\n    $permission = app(Permission::class)->findByName('wrong-guard-permission', 'admin');\n\n    expect(fn () => $this->testUserRole->hasPermissionTo($permission))\n        ->toThrow(GuardDoesNotMatch::class);\n});\n\nit('belongs to a guard', function () {\n    $role = app(Role::class)->create(['name' => 'admin', 'guard_name' => 'admin']);\n\n    expect($role->guard_name)->toEqual('admin');\n});\n\nit('belongs to the default guard by default', function () {\n    expect($this->testUserRole->guard_name)->toEqual(\n        $this->app['config']->get('auth.defaults.guard')\n    );\n});\n\nit('can change role class on runtime', function () {\n    $role = app(Role::class)->create(['name' => 'test-role-old']);\n    expect($role)->not->toBeInstanceOf(RuntimeRole::class);\n\n    $role->givePermissionTo('edit-articles');\n\n    app('config')->set('permission.models.role', RuntimeRole::class);\n    app()->bind(Role::class, RuntimeRole::class);\n    app(PermissionRegistrar::class)->setRoleClass(RuntimeRole::class);\n\n    $permission = app(Permission::class)->findByName('edit-articles');\n    expect($permission->roles[0])->toBeInstanceOf(RuntimeRole::class);\n    expect($permission->roles[0]->name)->toBe('test-role-old');\n\n    $role = app(Role::class)->create(['name' => 'test-role']);\n    expect($role)->toBeInstanceOf(RuntimeRole::class);\n\n    $this->testUser->assignRole('test-role');\n    expect($this->testUser->hasRole('test-role'))->toBeTrue();\n    expect($this->testUser->roles[0])->toBeInstanceOf(RuntimeRole::class);\n    expect($this->testUser->roles[0]->name)->toBe('test-role');\n});\n\nit('does not treat string \"0\" as empty when assigning role', function () {\n    app(Role::class)->create(['name' => '0']);\n\n    $this->testUser->assignRole('0');\n\n    expect($this->testUser->hasRole('0'))->toBeTrue();\n});\n"
  },
  {
    "path": "tests/Models/RoleWithNestingTest.php",
    "content": "<?php\n\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Role;\n\nbeforeEach(function () {\n    $this->setUpRoleNesting();\n\n    $this->parent_roles = [\n        'has_no_children' => Role::create(['name' => 'has_no_children']),\n        'has_1_child' => Role::create(['name' => 'has_1_child']),\n        'has_3_children' => Role::create(['name' => 'has_3_children']),\n    ];\n    $this->child_roles = [\n        'has_no_parents' => Role::create(['name' => 'has_no_parents']),\n        'has_1_parent' => Role::create(['name' => 'has_1_parent']),\n        'has_2_parents' => Role::create(['name' => 'has_2_parents']),\n        'third_child' => Role::create(['name' => 'third_child']),\n    ];\n\n    $this->parent_roles['has_1_child']->children()->attach($this->child_roles['has_2_parents']);\n    $this->parent_roles['has_3_children']->children()->attach([\n        $this->child_roles['has_2_parents']->getKey(),\n        $this->child_roles['has_1_parent']->getKey(),\n        $this->child_roles['third_child']->getKey(),\n    ]);\n});\n\nit('returns correct withCount of nested roles', function (string $role_group, string $index, string $relation, int $expectedCount) {\n    $role = $this->$role_group[$index];\n    $count_field_name = sprintf('%s_count', $relation);\n\n    $actualCount = (int) Role::withCount($relation)->find($role->getKey())->$count_field_name;\n\n    expect($actualCount)->toBe($expectedCount, sprintf('%s expects %d %s, %d found', $role->name, $expectedCount, $relation, $actualCount));\n})->with([\n    ['parent_roles', 'has_no_children', 'children', 0],\n    ['parent_roles', 'has_1_child', 'children', 1],\n    ['parent_roles', 'has_3_children', 'children', 3],\n    ['child_roles', 'has_no_parents', 'parents', 0],\n    ['child_roles', 'has_1_parent', 'parents', 1],\n    ['child_roles', 'has_2_parents', 'parents', 2],\n]);\n"
  },
  {
    "path": "tests/Models/WildcardRoleTest.php",
    "content": "<?php\n\nuse Spatie\\Permission\\Models\\Permission;\n\nbeforeEach(function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    Permission::create(['name' => 'other-permission']);\n    Permission::create(['name' => 'wrong-guard-permission', 'guard_name' => 'admin']);\n});\n\nit('can be given a permission', function () {\n    Permission::create(['name' => 'posts.*']);\n    $this->testUserRole->givePermissionTo('posts.*');\n\n    expect($this->testUserRole->hasPermissionTo('posts.create'))->toBeTrue();\n});\n\nit('can be given multiple permissions using an array', function () {\n    Permission::create(['name' => 'posts.*']);\n    Permission::create(['name' => 'news.*']);\n\n    $this->testUserRole->givePermissionTo(['posts.*', 'news.*']);\n\n    expect($this->testUserRole->hasPermissionTo('posts.create'))->toBeTrue();\n    expect($this->testUserRole->hasPermissionTo('news.create'))->toBeTrue();\n});\n\nit('can be given multiple permissions using multiple arguments', function () {\n    Permission::create(['name' => 'posts.*']);\n    Permission::create(['name' => 'news.*']);\n\n    $this->testUserRole->givePermissionTo('posts.*', 'news.*');\n\n    expect($this->testUserRole->hasPermissionTo('posts.edit.123'))->toBeTrue();\n    expect($this->testUserRole->hasPermissionTo('news.view.1'))->toBeTrue();\n});\n\nit('can be given a permission using objects', function () {\n    $this->testUserRole->givePermissionTo($this->testUserPermission);\n\n    expect($this->testUserRole->hasPermissionTo($this->testUserPermission))->toBeTrue();\n});\n\nit('returns false if it does not have the permission', function () {\n    expect($this->testUserRole->hasPermissionTo('other-permission'))->toBeFalse();\n});\n\nit('returns false if permission does not exist', function () {\n    expect($this->testUserRole->hasPermissionTo('doesnt-exist'))->toBeFalse();\n});\n\nit('returns false if it does not have a permission object', function () {\n    $permission = app(Permission::class)->findByName('other-permission');\n\n    expect($this->testUserRole->hasPermissionTo($permission))->toBeFalse();\n});\n\nit('creates permission object with findOrCreate if it does not have a permission object', function () {\n    $permission = app(Permission::class)->findOrCreate('another-permission');\n\n    expect($this->testUserRole->hasPermissionTo($permission))->toBeFalse();\n\n    $this->testUserRole->givePermissionTo($permission);\n\n    $this->testUserRole = $this->testUserRole->fresh();\n\n    expect($this->testUserRole->hasPermissionTo('another-permission'))->toBeTrue();\n});\n\nit('returns false when a permission of the wrong guard is passed in', function () {\n    $permission = app(Permission::class)->findByName('wrong-guard-permission', 'admin');\n\n    expect($this->testUserRole->hasPermissionTo($permission))->toBeFalse();\n});\n"
  },
  {
    "path": "tests/Pest.php",
    "content": "<?php\n\nuse Spatie\\Permission\\Tests\\TestSupport\\TestCase;\n\nuses(TestCase::class)->in(__DIR__);\n"
  },
  {
    "path": "tests/TestSupport/ContentPolicy.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport;\n\nuse Illuminate\\Contracts\\Auth\\Access\\Authorizable;\n\nclass ContentPolicy\n{\n    public function before(Authorizable $user, string $ability): ?bool\n    {\n        return $user->hasRole('testAdminRole', 'admin') ?: null;\n    }\n\n    public function view($user, $content)\n    {\n        return $user->id === $content->user_id;\n    }\n\n    public function update($user, $modelRecord): bool\n    {\n        return $user->id === $modelRecord->user_id || $user->can('edit-articles');\n    }\n}\n"
  },
  {
    "path": "tests/TestSupport/TestCase.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Foundation\\Console\\AboutCommand;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Cache;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Laravel\\Passport\\PassportServiceProvider;\nuse Orchestra\\Testbench\\TestCase as Orchestra;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Exceptions\\UnauthorizedException;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\PermissionServiceProvider;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Admin;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Client;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Role as TestRole;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nclass TestCase extends Orchestra\n{\n    /** @var \\Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User */\n    protected $testUser;\n\n    /** @var \\Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Admin */\n    protected $testAdmin;\n\n    /** @var \\Spatie\\Permission\\Models\\Role */\n    protected $testUserRole;\n\n    /** @var \\Spatie\\Permission\\Models\\Role */\n    protected $testAdminRole;\n\n    /** @var \\Spatie\\Permission\\Models\\Permission */\n    protected $testUserPermission;\n\n    /** @var \\Spatie\\Permission\\Models\\Permission */\n    protected $testAdminPermission;\n\n    protected static $migration;\n\n    protected static $customMigration;\n\n    protected Client $testClient;\n\n    protected \\Spatie\\Permission\\Models\\Permission $testClientPermission;\n\n    protected \\Spatie\\Permission\\Models\\Role $testClientRole;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! self::$migration) {\n            $this->prepareMigration();\n        }\n\n        // Note: this also flushes the cache from within the migration\n        $this->setUpDatabase($this->app);\n\n        $this->setUpBaseTestPermissions($this->app);\n\n        $this->setUpRoutes();\n    }\n\n    protected function tearDown(): void\n    {\n        parent::tearDown();\n\n        if (method_exists(AboutCommand::class, 'flushState')) {\n            AboutCommand::flushState();\n        }\n    }\n\n    /**\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     */\n    protected function getPackageProviders($app): array\n    {\n        return [\n            PermissionServiceProvider::class,\n            PassportServiceProvider::class,\n        ];\n    }\n\n    /**\n     * Set up the environment.\n     *\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     */\n    protected function getEnvironmentSetUp($app)\n    {\n        Model::preventLazyLoading();\n        $app['config']->set('permission.register_permission_check_method', true);\n        $app['config']->set('permission.teams', false);\n        $app['config']->set('permission.testing', true); // fix sqlite\n        $app['config']->set('permission.column_names.model_morph_key', 'model_test_id');\n        $app['config']->set('permission.column_names.team_foreign_key', 'team_test_id');\n        $app['config']->set('database.default', 'sqlite');\n        $app['config']->set('database.connections.sqlite', [\n            'driver' => 'sqlite',\n            'database' => ':memory:',\n            'prefix' => '',\n        ]);\n        $app['config']->set('permission.column_names.role_pivot_key', 'role_test_id');\n        $app['config']->set('permission.column_names.permission_pivot_key', 'permission_test_id');\n        $app['config']->set('view.paths', [__DIR__.'/resources/views']);\n\n        // ensure api guard exists, since we use it for testing multi-guard support\n        $app['config']->set('auth.guards.api', ['driver' => 'session', 'provider' => 'users']);\n\n        // Set-up admin guard\n        $app['config']->set('auth.guards.admin', ['driver' => 'session', 'provider' => 'admins']);\n        $app['config']->set('auth.providers.admins', ['driver' => 'eloquent', 'model' => Admin::class]);\n        // Use test User model for users provider\n        $app['config']->set('auth.providers.users.model', User::class);\n\n        $app['config']->set('cache.prefix', 'spatie_tests---');\n        $app['config']->set('cache.default', getenv('CACHE_DRIVER') ?: 'array');\n\n        // FOR MANUAL TESTING OF ALTERNATE CACHE STORES:\n        // $app['config']->set('cache.default', 'array');\n        // Laravel supports: array, database, file\n        // requires extensions: memcached, redis, dynamodb, octane\n    }\n\n    /**\n     * Set up the database.\n     *\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     */\n    protected function setUpDatabase($app)\n    {\n        $schema = $app['db']->connection()->getSchemaBuilder();\n\n        $schema->create('users', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('email');\n            $table->softDeletes();\n        });\n\n        $schema->create('admins', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('email');\n        });\n\n        $schema->create('content', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('content');\n            $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();\n            $table->timestamps();\n        });\n\n        if (Cache::getStore() instanceof \\Illuminate\\Cache\\DatabaseStore ||\n            $app[PermissionRegistrar::class]->getCacheStore() instanceof \\Illuminate\\Cache\\DatabaseStore) {\n            $this->createCacheTable();\n        }\n\n        self::$migration->up();\n\n        $this->testUser = User::create(['email' => 'test@user.com']);\n        $this->testAdmin = Admin::create(['email' => 'admin@user.com']);\n    }\n\n    /**\n     * Set up initial roles and permissions used in many tests\n     *\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     */\n    protected function setUpBaseTestPermissions($app): void\n    {\n        $this->testUserRole = $app[Role::class]->create(['name' => 'testRole']);\n        $app[Role::class]->create(['name' => 'testRole2']);\n        $this->testAdminRole = $app[Role::class]->create(['name' => 'testAdminRole', 'guard_name' => 'admin']);\n        $this->testUserPermission = $app[Permission::class]->create(['name' => 'edit-articles']);\n        $app[Permission::class]->create(['name' => 'edit-news']);\n        $app[Permission::class]->create(['name' => 'edit-blog']);\n        $this->testAdminPermission = $app[Permission::class]->create([\n            'name' => 'admin-permission',\n            'guard_name' => 'admin',\n        ]);\n        $app[Permission::class]->create(['name' => 'Edit News']);\n    }\n\n    public function setUpPassport(): void\n    {\n        $app = $this->app;\n\n        $app['config']->set('permission.use_passport_client_credentials', true);\n        $app['config']->set('auth.guards.api', ['driver' => 'passport', 'provider' => 'users']);\n\n        // mimic passport:install (must load migrations using our own call to loadMigrationsFrom() else rollbacks won't occur, and migrations will be left in skeleton directory\n        // $this->artisan('passport:keys');\n        $this->loadMigrationsFrom(__DIR__.'/../../vendor/laravel/passport/database/migrations/');\n        $provider = in_array('users', array_keys(config('auth.providers'))) ? 'users' : null;\n        $this->artisan('passport:client', ['--personal' => true, '--name' => config('app.name').' Personal Access Client', '--provider' => $provider]);\n        $this->artisan('passport:client', ['--password' => true, '--name' => config('app.name').' Password Grant Client', '--provider' => $provider]);\n\n        $this->testClient = Client::create(['name' => 'Test', 'redirect_uris' => ['https://example.com'], 'grant_types' => [], 'revoked' => 0]);\n        $this->testClientRole = $app[Role::class]->create(['name' => 'clientRole', 'guard_name' => 'api']);\n        $this->testClientPermission = $app[Permission::class]->create(['name' => 'edit-posts', 'guard_name' => 'api']);\n    }\n\n    private function prepareMigration(): void\n    {\n        $migration = str_replace(\n            [\n                '$table->id(); // permission id',\n                '$table->id(); // role id',\n                'references(\\'id\\') // permission id',\n                'references(\\'id\\') // role id',\n                'unsignedBigInteger($pivotRole)',\n                'unsignedBigInteger($pivotPermission)',\n            ],\n            [\n                '$table->uuid(\\'permission_test_id\\');',\n                '$table->uuid(\\'role_test_id\\');',\n                'references(\\'permission_test_id\\')',\n                'references(\\'role_test_id\\')',\n                'uuid($pivotRole)->nullable(false)',\n                'uuid($pivotPermission)->nullable(false)',\n            ],\n            file_get_contents(__DIR__.'/../../database/migrations/create_permission_tables.php.stub')\n        );\n\n        file_put_contents(__DIR__.'/CreatePermissionCustomTables.php', $migration);\n\n        self::$migration = require __DIR__.'/../../database/migrations/create_permission_tables.php.stub';\n\n        self::$customMigration = require __DIR__.'/CreatePermissionCustomTables.php';\n    }\n\n    public function setUpTeams(): void\n    {\n        self::$migration->down();\n\n        config()->set('permission.teams', true);\n\n        self::$migration->up();\n\n        app(PermissionRegistrar::class)->initializeCache();\n\n        $this->setUpBaseTestPermissions($this->app);\n\n        setPermissionsTeamId(1);\n    }\n\n    public function setUpCustomModels(): void\n    {\n        self::$migration->down();\n\n        $registrar = app(PermissionRegistrar::class);\n        $registrar->setPermissionClass(\\Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Permission::class);\n        $registrar->setRoleClass(\\Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Role::class);\n\n        self::$customMigration->up();\n\n        $schema = $this->app['db']->connection()->getSchemaBuilder();\n\n        $schema->table(config('permission.table_names.roles'), function (Blueprint $table) {\n            $table->softDeletes();\n        });\n        $schema->table(config('permission.table_names.permissions'), function (Blueprint $table) {\n            $table->softDeletes();\n        });\n\n        $registrar->initializeCache();\n\n        $this->setUpBaseTestPermissions($this->app);\n    }\n\n    public function setUpRoleNesting(): void\n    {\n        $this->setUpCustomModels();\n\n        $tableRoles = config('permission.table_names.roles');\n\n        $this->app['db']->connection()->getSchemaBuilder()->create(TestRole::HIERARCHY_TABLE, function ($table) use ($tableRoles) {\n            $table->id();\n            $table->uuid('parent_id');\n            $table->uuid('child_id');\n            $table->foreign('parent_id')->references('role_test_id')->on($tableRoles);\n            $table->foreign('child_id')->references('role_test_id')->on($tableRoles);\n        });\n    }\n\n    protected function reloadPermissions(): void\n    {\n        app(PermissionRegistrar::class)->forgetCachedPermissions();\n    }\n\n    public function createCacheTable(): void\n    {\n        Schema::create('cache', function ($table) {\n            $table->string('key')->unique();\n            $table->text('value');\n            $table->integer('expiration');\n        });\n    }\n\n    /**\n     * Create routes to test authentication with guards.\n     */\n    public function setUpRoutes(): void\n    {\n        Route::middleware('auth:api')->get('/check-api-guard-permission', function (Request $request) {\n            return [\n                'status' => $request->user()->hasPermissionTo('do_that'),\n            ];\n        });\n    }\n\n    // //// TEST HELPERS\n    public function runMiddleware($middleware, $permission, $guard = null, bool $client = false)\n    {\n        $request = new Request;\n        if ($client) {\n            $request->headers->set('Authorization', 'Bearer '.str()->random(30));\n        }\n\n        try {\n            return $middleware->handle($request, function () {\n                return (new Response)->setContent('<html></html>');\n            }, $permission, $guard)->status();\n        } catch (UnauthorizedException $e) {\n            return $e->getStatusCode();\n        }\n    }\n\n    public function getLastRouteMiddlewareFromRouter($router)\n    {\n        return last($router->getRoutes()->get())->middleware();\n    }\n\n    public function getRouter()\n    {\n        return app('router');\n    }\n\n    public function getRouteResponse()\n    {\n        return function () {\n            return (new Response)->setContent('<html></html>');\n        };\n    }\n\n    protected function getLaravelVersion(): float\n    {\n        return (float) app()->version();\n    }\n}\n"
  },
  {
    "path": "tests/TestSupport/TestHelper.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport;\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\nuse Symfony\\Component\\HttpKernel\\Exception\\HttpException;\n\nclass TestHelper\n{\n    /**\n     * @param  string  $middleware\n     * @param  object  $parameter\n     * @return int\n     */\n    public function testMiddleware($middleware, $parameter)\n    {\n        try {\n            return $middleware->handle(new Request, function () {\n                return (new Response)->setContent('<html></html>');\n            }, $parameter)->status();\n        } catch (HttpException $e) {\n            return $e->getStatusCode();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/Admin.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nclass Admin extends User\n{\n    protected $table = 'admins';\n\n    protected $touches = ['roles', 'permissions'];\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/Client.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nuse Illuminate\\Contracts\\Auth\\Access\\Authorizable as AuthorizableContract;\nuse Illuminate\\Foundation\\Auth\\Access\\Authorizable;\nuse Laravel\\Passport\\Client as BaseClient;\nuse Spatie\\Permission\\Traits\\HasRoles;\n\nclass Client extends BaseClient implements AuthorizableContract\n{\n    use Authorizable;\n    use HasRoles;\n\n    /**\n     * Required to make clear that the client requires the api guard\n     */\n    protected string $guard_name = 'api';\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/Content.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Content extends Model\n{\n    protected $guarded = [];\n\n    protected $table = 'content';\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/Manager.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nclass Manager extends User\n{\n    // this function is added here to support the unit tests verifying it works\n    // When present, it takes precedence over the $guard_name property.\n    public function guardName(): string\n    {\n        return 'jwt';\n    }\n\n    // intentionally different property value for the sake of unit tests\n    protected string $guard_name = 'web';\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/Permission.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Str;\n\nclass Permission extends \\Spatie\\Permission\\Models\\Permission\n{\n    use SoftDeletes;\n\n    protected $primaryKey = 'permission_test_id';\n\n    protected $visible = [\n        'permission_test_id',\n        'name',\n    ];\n\n    protected static function boot()\n    {\n        parent::boot();\n        static::creating(static function ($model) {\n            if (empty($model->{$model->getKeyName()})) {\n                $model->{$model->getKeyName()} = Str::uuid()->toString();\n            }\n        });\n    }\n\n    public function getIncrementing(): bool\n    {\n        return false;\n    }\n\n    public function getKeyType(): string\n    {\n        return 'string';\n    }\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/Role.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Str;\n\nclass Role extends \\Spatie\\Permission\\Models\\Role\n{\n    use SoftDeletes;\n\n    protected $primaryKey = 'role_test_id';\n\n    protected $visible = [\n        'role_test_id',\n        'name',\n    ];\n\n    const HIERARCHY_TABLE = 'roles_hierarchy';\n\n    public function getNameAttribute(): \\BackedEnum|string\n    {\n        $name = $this->attributes['name'];\n\n        if (str_contains($name, 'casted_enum')) {\n            return TestRolePermissionsEnum::from($name);\n        }\n\n        return $name;\n    }\n\n    public function parents(): BelongsToMany\n    {\n        return $this->belongsToMany(\n            static::class,\n            static::HIERARCHY_TABLE,\n            'child_id',\n            'parent_id');\n    }\n\n    public function children(): BelongsToMany\n    {\n        return $this->belongsToMany(\n            static::class,\n            static::HIERARCHY_TABLE,\n            'parent_id',\n            'child_id');\n    }\n\n    protected static function boot()\n    {\n        parent::boot();\n        static::creating(static function ($model) {\n            if (empty($model->{$model->getKeyName()})) {\n                $model->{$model->getKeyName()} = Str::uuid()->toString();\n            }\n        });\n    }\n\n    public function getIncrementing(): bool\n    {\n        return false;\n    }\n\n    public function getKeyType(): string\n    {\n        return 'string';\n    }\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/RuntimeRole.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nclass RuntimeRole extends \\Spatie\\Permission\\Models\\Role\n{\n    protected $visible = [\n        'id',\n        'name',\n    ];\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/SoftDeletingUser.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass SoftDeletingUser extends User\n{\n    use SoftDeletes;\n\n    protected string $guard_name = 'web';\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/TestRolePermissionsEnum.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nuse Illuminate\\Support\\Str;\n\nenum TestRolePermissionsEnum: string\n{\n    // case NAME = 'value';\n    // case NAMEINAPP = 'name-in-database';\n\n    case Writer = 'writer';\n    case Editor = 'editor';\n    case UserManager = 'user-manager';\n    case Admin = 'administrator';\n    case CastedEnum1 = 'casted_enum-1';\n    case CastedEnum2 = 'casted_enum-2';\n\n    case ViewArticles = 'view articles';\n    case EditArticles = 'edit articles';\n\n    case WildcardArticlesCreator = 'articles.edit,view,create';\n    case WildcardNewsEverything = 'news.*';\n    case WildcardPostsEverything = 'posts.*';\n\n    case WildcardPostsCreate = 'posts.create';\n    case WildcardArticlesView = 'articles.view';\n    case WildcardProjectsView = 'projects.view';\n\n    // extra helper to allow for greater customization of displayed values, without disclosing the name/value data directly\n    public function label(): string\n    {\n        return match ($this) {\n            self::Writer => 'Writers',\n            self::Editor => 'Editors',\n            self::UserManager => 'User Managers',\n            self::Admin => 'Admins',\n\n            self::ViewArticles => 'View Articles',\n            self::EditArticles => 'Edit Articles',\n\n            default => Str::words($this->value),\n        };\n    }\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/User.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nuse Spatie\\Permission\\Traits\\HasRoles;\n\nclass User extends UserWithoutHasRoles\n{\n    use HasRoles;\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/UserWithoutHasRoles.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nuse Illuminate\\Auth\\Authenticatable;\nuse Illuminate\\Contracts\\Auth\\Access\\Authorizable as AuthorizableContract;\nuse Illuminate\\Contracts\\Auth\\Authenticatable as AuthenticatableContract;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Foundation\\Auth\\Access\\Authorizable;\n\nclass UserWithoutHasRoles extends Model implements AuthenticatableContract, AuthorizableContract\n{\n    use Authenticatable;\n    use Authorizable;\n\n    protected $fillable = ['email'];\n\n    public $timestamps = false;\n\n    protected $table = 'users';\n}\n"
  },
  {
    "path": "tests/TestSupport/TestModels/WildcardPermission.php",
    "content": "<?php\n\nnamespace Spatie\\Permission\\Tests\\TestSupport\\TestModels;\n\nuse Spatie\\Permission\\WildcardPermission as BaseWildcardPermission;\n\nclass WildcardPermission extends BaseWildcardPermission\n{\n    /** @var string */\n    public const WILDCARD_TOKEN = '@';\n\n    /** @var non-empty-string */\n    public const PART_DELIMITER = ':';\n\n    /** @var non-empty-string */\n    public const SUBPART_DELIMITER = ';';\n}\n"
  },
  {
    "path": "tests/TestSupport/resources/views/can.blade.php",
    "content": "@can($permission, $guard ?? null)\nhas permission\n@else\ndoes not have permission\n@endcan\n"
  },
  {
    "path": "tests/TestSupport/resources/views/guardHasAllRoles.blade.php",
    "content": "@hasallroles($roles, $guard)\ndoes have all of the given roles\n@else\ndoes not have all of the given roles\n@endhasallroles\n"
  },
  {
    "path": "tests/TestSupport/resources/views/guardHasAllRolesArray.blade.php",
    "content": "@hasallroles(['super-admin', 'moderator'], $guard)\ndoes have all of the given roles\n@else\ndoes not have all of the given roles\n@endhasallroles\n"
  },
  {
    "path": "tests/TestSupport/resources/views/guardHasAllRolesPipe.blade.php",
    "content": "@hasallroles(\"super-admin|moderator\", $guard)\ndoes have all of the given roles\n@else\ndoes not have all of the given roles\n@endhasallroles\n"
  },
  {
    "path": "tests/TestSupport/resources/views/guardHasAnyRole.blade.php",
    "content": "@hasanyrole($roles, $guard)\ndoes have some of the roles\n@else\ndoes not have any of the given roles\n@endhasanyrole\n"
  },
  {
    "path": "tests/TestSupport/resources/views/guardHasAnyRolePipe.blade.php",
    "content": "@hasanyrole(\"super-admin|moderator\", $guard)\ndoes have some of the roles\n@else\ndoes not have any of the given roles\n@endhasanyrole\n"
  },
  {
    "path": "tests/TestSupport/resources/views/guardHasRole.blade.php",
    "content": "@hasrole($role, $guard)\nhas role\n@else\ndoes not have role\n@endhasrole\n"
  },
  {
    "path": "tests/TestSupport/resources/views/guardRole.blade.php",
    "content": "@role($role, $guard)\nhas role for guard\n@else\ndoes not have role for guard\n@endrole\n"
  },
  {
    "path": "tests/TestSupport/resources/views/guardunlessrole.blade.php",
    "content": "@unlessrole($role, $guard)\ndoes not have role\n@else\nhas role\n@endunlessrole\n"
  },
  {
    "path": "tests/TestSupport/resources/views/hasAllRoles.blade.php",
    "content": "@hasallroles($roles)\ndoes have all of the given roles\n@else\ndoes not have all of the given roles\n@endhasallroles\n"
  },
  {
    "path": "tests/TestSupport/resources/views/hasAnyRole.blade.php",
    "content": "@hasanyrole($roles)\ndoes have some of the roles\n@else\ndoes not have any of the given roles\n@endhasanyrole\n"
  },
  {
    "path": "tests/TestSupport/resources/views/hasRole.blade.php",
    "content": "@hasrole($role)\nhas role\n@else\ndoes not have role\n@endhasrole\n"
  },
  {
    "path": "tests/TestSupport/resources/views/haspermission.blade.php",
    "content": "@haspermission($permission, $guard ?? null)\nhas permission\n@elsehaspermission($elsepermission, $guard ?? null)\nhas else permission\n@else\ndoes not have permission\n@endhaspermission\n"
  },
  {
    "path": "tests/TestSupport/resources/views/role.blade.php",
    "content": "@role($role)\nhas role\n@elserole($elserole)\nhas else role\n@else\ndoes not have role\n@endrole\n"
  },
  {
    "path": "tests/TestSupport/resources/views/unlessrole.blade.php",
    "content": "@unlessrole($role)\ndoes not have role\n@else\nhas role\n@endunlessrole\n"
  },
  {
    "path": "tests/Traits/HasPermissionsTest.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Support\\Facades\\Event;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Events\\PermissionAttachedEvent;\nuse Spatie\\Permission\\Events\\PermissionDetachedEvent;\nuse Spatie\\Permission\\Exceptions\\GuardDoesNotMatch;\nuse Spatie\\Permission\\Exceptions\\PermissionDoesNotExist;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\SoftDeletingUser;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nit('can assign a permission to a user', function () {\n    $this->testUser->givePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeTrue();\n});\n\nit('can assign a permission to a user with a non default guard', function () {\n    $testUserPermission = app(Permission::class)->create([\n        'name' => 'edit-articles',\n        'guard_name' => 'api',\n    ]);\n\n    $this->testUser->givePermissionTo($testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($testUserPermission))->toBeTrue();\n});\n\nit('throws an exception when assigning a permission that does not exist', function () {\n    expect(fn () => $this->testUser->givePermissionTo('permission-does-not-exist'))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when assigning a permission to a user from a different guard', function () {\n    expect(fn () => $this->testUser->givePermissionTo($this->testAdminPermission))\n        ->toThrow(GuardDoesNotMatch::class);\n\n    expect(fn () => $this->testUser->givePermissionTo('admin-permission'))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('can revoke a permission from a user', function () {\n    $this->testUser->givePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeTrue();\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeFalse();\n});\n\nit('can assign and remove a permission using enums', function () {\n    $enum = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::ViewArticles;\n\n    $permission = app(Permission::class)->findOrCreate($enum->value, 'web');\n\n    $this->testUser->givePermissionTo($enum);\n\n    expect($this->testUser->hasPermissionTo($enum))->toBeTrue();\n    expect($this->testUser->hasAnyPermission($enum))->toBeTrue();\n    expect($this->testUser->hasDirectPermission($enum))->toBeTrue();\n\n    $this->testUser->revokePermissionTo($enum);\n\n    expect($this->testUser->hasPermissionTo($enum))->toBeFalse();\n    expect($this->testUser->hasAnyPermission($enum))->toBeFalse();\n    expect($this->testUser->hasDirectPermission($enum))->toBeFalse();\n});\n\nit('can scope users using enums', function () {\n    $enum1 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::ViewArticles;\n    $enum2 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::EditArticles;\n    $permission1 = app(Permission::class)->findOrCreate($enum1->value, 'web');\n    $permission2 = app(Permission::class)->findOrCreate($enum2->value, 'web');\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo([$enum1, $enum2]);\n    $this->testUserRole->givePermissionTo($enum2);\n    $user2->assignRole('testRole');\n\n    $scopedUsers1 = User::permission($enum2)->get();\n    $scopedUsers2 = User::permission([$enum1])->get();\n    $scopedUsers3 = User::withoutPermission([$enum1])->get();\n    $scopedUsers4 = User::withoutPermission([$enum2])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n    expect($scopedUsers4->count())->toEqual(1);\n});\n\nit('can scope users using a string', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n\n    $scopedUsers1 = User::permission('edit-articles')->get();\n    $scopedUsers2 = User::permission(['edit-news'])->get();\n    $scopedUsers3 = User::withoutPermission('edit-news')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using a int', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo([1, 2]);\n    $this->testUserRole->givePermissionTo(1);\n    $user2->assignRole('testRole');\n\n    $scopedUsers1 = User::permission(1)->get();\n    $scopedUsers2 = User::permission([2])->get();\n    $scopedUsers3 = User::withoutPermission([2])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using an array', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::permission(['edit-articles', 'edit-news'])->get();\n    $scopedUsers2 = User::permission(['edit-news'])->get();\n    $scopedUsers3 = User::withoutPermission(['edit-news'])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using a collection', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::permission(collect(['edit-articles', 'edit-news']))->get();\n    $scopedUsers2 = User::permission(collect(['edit-news']))->get();\n    $scopedUsers3 = User::withoutPermission(collect(['edit-news']))->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using an object', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user1->givePermissionTo($this->testUserPermission->name);\n\n    $scopedUsers1 = User::permission($this->testUserPermission)->get();\n    $scopedUsers2 = User::permission([$this->testUserPermission])->get();\n    $scopedUsers3 = User::permission(collect([$this->testUserPermission]))->get();\n    $scopedUsers4 = User::withoutPermission(collect([$this->testUserPermission]))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(1);\n    expect($scopedUsers4->count())->toEqual(0);\n});\n\nit('can scope users without direct permissions only role', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::permission('edit-articles')->get();\n    $scopedUsers2 = User::withoutPermission('edit-articles')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('can scope users with only direct permission', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-news']);\n    $user2->givePermissionTo(['edit-articles', 'edit-news']);\n\n    $scopedUsers1 = User::permission('edit-news')->get();\n    $scopedUsers2 = User::withoutPermission('edit-news')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('throws an exception when calling hasPermissionTo with an invalid type', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasPermissionTo(new \\stdClass))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when calling hasPermissionTo with null', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasPermissionTo(null))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when calling hasDirectPermission with an invalid type', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasDirectPermission(new \\stdClass))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when calling hasDirectPermission with null', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasDirectPermission(null))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when trying to scope a non existing permission', function () {\n    expect(fn () => User::permission('not defined permission')->get())\n        ->toThrow(PermissionDoesNotExist::class);\n\n    expect(fn () => User::withoutPermission('not defined permission')->get())\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when trying to scope a permission from another guard', function () {\n    expect(fn () => User::permission('testAdminPermission')->get())\n        ->toThrow(PermissionDoesNotExist::class);\n\n    expect(fn () => User::withoutPermission('testAdminPermission')->get())\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('doesnt detach permissions when user soft deleting', function () {\n    $user = SoftDeletingUser::create(['email' => 'test@example.com']);\n    $user->givePermissionTo(['edit-news']);\n    $user->delete();\n\n    $user = SoftDeletingUser::withTrashed()->find($user->id);\n\n    expect($user->hasPermissionTo('edit-news'))->toBeTrue();\n});\n\nit('can give and revoke multiple permissions', function () {\n    $this->testUserRole->givePermissionTo(['edit-articles', 'edit-news']);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(2);\n\n    $this->testUserRole->revokePermissionTo(['edit-articles', 'edit-news']);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(0);\n});\n\nit('can give and revoke permissions models array', function () {\n    $models = [app(Permission::class)::where('name', 'edit-articles')->first(), app(Permission::class)::where('name', 'edit-news')->first()];\n\n    $this->testUserRole->givePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(2);\n\n    $this->testUserRole->revokePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(0);\n});\n\nit('can give and revoke permissions models collection', function () {\n    $models = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-news'])->get();\n\n    $this->testUserRole->givePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(2);\n\n    $this->testUserRole->revokePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(0);\n});\n\nit('can determine that the user does not have a permission', function () {\n    expect($this->testUser->hasPermissionTo('edit-articles'))->toBeFalse();\n});\n\nit('throws an exception when the permission does not exist', function () {\n    expect(fn () => $this->testUser->hasPermissionTo('does-not-exist'))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when the permission does not exist for this guard', function () {\n    expect(fn () => $this->testUser->hasPermissionTo('does-not-exist', 'web'))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n\nit('can reject a user that does not have any permissions at all', function () {\n    $user = new User;\n\n    expect($user->hasPermissionTo('edit-articles'))->toBeFalse();\n});\n\nit('can determine that the user has any of the permissions directly', function () {\n    expect($this->testUser->hasAnyPermission('edit-articles'))->toBeFalse();\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAnyPermission('edit-news', 'edit-articles'))->toBeTrue();\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasAnyPermission('edit-articles', 'edit-news'))->toBeTrue();\n    expect($this->testUser->hasAnyPermission('edit-blog', 'Edit News', ['Edit News']))->toBeFalse();\n});\n\nit('can determine that the user has any of the permissions directly using an array', function () {\n    expect($this->testUser->hasAnyPermission(['edit-articles']))->toBeFalse();\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAnyPermission(['edit-news', 'edit-articles']))->toBeTrue();\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasAnyPermission(['edit-articles', 'edit-news']))->toBeTrue();\n});\n\nit('can determine that the user has any of the permissions via role', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAnyPermission('edit-news', 'edit-articles'))->toBeTrue();\n    expect($this->testUser->hasAnyPermission('edit-blog', 'Edit News', ['Edit News']))->toBeFalse();\n});\n\nit('can determine that the user has all of the permissions directly', function () {\n    $this->testUser->givePermissionTo('edit-articles', 'edit-news');\n\n    expect($this->testUser->hasAllPermissions('edit-articles', 'edit-news'))->toBeTrue();\n\n    $this->testUser->revokePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAllPermissions('edit-articles', 'edit-news'))->toBeFalse();\n    expect($this->testUser->hasAllPermissions(['edit-articles', 'edit-news'], 'edit-blog'))->toBeFalse();\n});\n\nit('can determine that the user has all of the permissions directly using an array', function () {\n    expect($this->testUser->hasAllPermissions(['edit-articles', 'edit-news']))->toBeFalse();\n\n    $this->testUser->revokePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAllPermissions(['edit-news', 'edit-articles']))->toBeFalse();\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasAllPermissions(['edit-articles', 'edit-news']))->toBeFalse();\n});\n\nit('can determine that the user has all of the permissions via role', function () {\n    $this->testUserRole->givePermissionTo('edit-articles', 'edit-news');\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAllPermissions('edit-articles', 'edit-news'))->toBeTrue();\n});\n\nit('can determine that user has direct permission', function () {\n    $this->testUser->givePermissionTo('edit-articles');\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->getDirectPermissions()->pluck('name'))->toEqual(collect(['edit-articles']));\n\n    $this->testUser->revokePermissionTo('edit-articles');\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeFalse();\n\n    $this->testUser->assignRole('testRole');\n    $this->testUserRole->givePermissionTo('edit-articles');\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeFalse();\n});\n\nit('can list all the permissions via roles of user', function () {\n    $roleModel = app(Role::class);\n    $roleModel->findByName('testRole2')->givePermissionTo('edit-news');\n\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    expect($this->testUser->getPermissionsViaRoles()->pluck('name')->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n});\n\nit('can list all the coupled permissions both directly and via roles', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->getAllPermissions()->pluck('name')->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n});\n\nit('can sync multiple permissions', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->syncPermissions('edit-articles', 'edit-blog');\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-news'))->toBeFalse();\n});\n\nit('can avoid sync duplicated permissions', function () {\n    $this->testUser->syncPermissions('edit-articles', 'edit-blog', 'edit-blog');\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n});\n\nit('can avoid detach on permission that does not exist sync', function () {\n    $this->testUser->syncPermissions('edit-articles');\n\n    expect(fn () => $this->testUser->syncPermissions('permission-does-not-exist'))\n        ->toThrow(PermissionDoesNotExist::class);\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->checkPermissionTo('permission-does-not-exist'))->toBeFalse();\n});\n\nit('can sync multiple permissions by id', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $ids = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-blog'])->pluck($this->testUserPermission->getKeyName());\n\n    $this->testUser->syncPermissions($ids);\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-news'))->toBeFalse();\n});\n\nit('sync permission ignores null inputs', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $ids = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-blog'])->pluck($this->testUserPermission->getKeyName());\n\n    $ids->push(null);\n\n    $this->testUser->syncPermissions($ids);\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-news'))->toBeFalse();\n});\n\nit('sync permission error does not detach permissions', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    expect(fn () => $this->testUser->syncPermissions('edit-articles', 'permission-that-does-not-exist'))\n        ->toThrow(PermissionDoesNotExist::class);\n\n    expect($this->testUser->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('does not remove already associated permissions when assigning new permissions', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('does not throw an exception when assigning a permission that is already assigned', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    expect($this->testUser->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('can sync permissions to a model that is not persisted', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncPermissions('edit-articles');\n    $user->save();\n    $user->save(); // test save same model twice\n\n    expect($user->hasPermissionTo('edit-articles'))->toBeTrue();\n\n    $user->syncPermissions('edit-articles');\n    expect($user->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($user->fresh()->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n\nit('does not run unnecessary sqls when assigning new permissions', function () {\n    $permission2 = app(Permission::class)->where('name', ['edit-news'])->first();\n\n    DB::enableQueryLog();\n    $this->testUser->syncPermissions($this->testUserPermission, $permission2);\n    DB::disableQueryLog();\n\n    $necessaryQueriesCount = 2;\n\n    expect(DB::getQueryLog())->toHaveCount($necessaryQueriesCount);\n});\n\nit('calling givePermissionTo before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->givePermissionTo('edit-news');\n    $user->save();\n\n    $user2 = new User(['email' => 'test2@user.com']);\n    $user2->givePermissionTo('edit-articles');\n\n    DB::enableQueryLog();\n    $user2->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasPermissionTo('edit-news'))->toBeTrue();\n    expect($user->fresh()->hasPermissionTo('edit-articles'))->toBeFalse();\n\n    expect($user2->fresh()->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($user2->fresh()->hasPermissionTo('edit-news'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('calling syncPermissions before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncPermissions('edit-news');\n    $user->save();\n\n    $user2 = new User(['email' => 'test2@user.com']);\n    $user2->syncPermissions('edit-articles');\n\n    DB::enableQueryLog();\n    $user2->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasPermissionTo('edit-news'))->toBeTrue();\n    expect($user->fresh()->hasPermissionTo('edit-articles'))->toBeFalse();\n\n    expect($user2->fresh()->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($user2->fresh()->hasPermissionTo('edit-news'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('can retrieve permission names', function () {\n    $this->testUser->givePermissionTo('edit-news', 'edit-articles');\n    expect($this->testUser->getPermissionNames()->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n});\n\nit('can check many direct permissions', function () {\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n    expect($this->testUser->hasAllDirectPermissions(['edit-news', 'edit-articles']))->toBeTrue();\n    expect($this->testUser->hasAllDirectPermissions('edit-news', 'edit-articles'))->toBeTrue();\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-news', 'edit-blog']))->toBeFalse();\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-news'], 'edit-blog'))->toBeFalse();\n});\n\nit('can check if there is any of the direct permissions given', function () {\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n    expect($this->testUser->hasAnyDirectPermission(['edit-news', 'edit-blog']))->toBeTrue();\n    expect($this->testUser->hasAnyDirectPermission('edit-news', 'edit-blog'))->toBeTrue();\n    expect($this->testUser->hasAnyDirectPermission('edit-blog', 'Edit News', ['Edit News']))->toBeFalse();\n});\n\nit('can check permission based on logged in user guard', function () {\n    $this->testUser->givePermissionTo(app(Permission::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'api',\n    ]));\n    $response = $this->actingAs($this->testUser, 'api')\n        ->json('GET', '/check-api-guard-permission');\n    $response->assertJson([\n        'status' => true,\n    ]);\n});\n\nit('can reject permission based on logged in user guard', function () {\n    $unassignedPermission = app(Permission::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'api',\n    ]);\n\n    $assignedPermission = app(Permission::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'web',\n    ]);\n\n    $this->testUser->givePermissionTo($assignedPermission);\n    $response = $this->withExceptionHandling()\n        ->actingAs($this->testUser, 'api')\n        ->json('GET', '/check-api-guard-permission');\n    $response->assertJson([\n        'status' => false,\n    ]);\n});\n\nit('fires an event when a permission is added', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n\n    $ids = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-news'])\n        ->pluck($this->testUserPermission->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(PermissionAttachedEvent::class, function ($event) use ($ids) {\n        return $event->model instanceof User\n            && $event->model->hasPermissionTo('edit-news')\n            && $event->model->hasPermissionTo('edit-articles')\n            && $ids === $event->permissionsOrIds;\n    });\n});\n\nit('does not fire an event when events are not enabled', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', false);\n\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n\n    $ids = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-news'])\n        ->pluck($this->testUserPermission->getKeyName())\n        ->toArray();\n\n    Event::assertNotDispatched(PermissionAttachedEvent::class);\n});\n\nit('fires an event when a permission is removed', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $permissions = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-news'])->get();\n\n    $this->testUser->givePermissionTo($permissions);\n\n    $this->testUser->revokePermissionTo($permissions);\n\n    Event::assertDispatched(PermissionDetachedEvent::class, function ($event) use ($permissions) {\n        return $event->model instanceof User\n            && ! $event->model->hasPermissionTo('edit-news')\n            && ! $event->model->hasPermissionTo('edit-articles')\n            && $event->permissionsOrIds === $permissions;\n    });\n});\n\nit('can be given a permission on role when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    $testRole = app(Role::class)->with('permissions')->get()->first();\n\n    $testRole->givePermissionTo('edit-articles');\n\n    expect($testRole->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n\nit('can be given a permission on user when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    User::create(['email' => 'other@user.com']);\n    $testUser = User::with('permissions')->get()->first();\n\n    $testUser->givePermissionTo('edit-articles');\n\n    expect($testUser->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n"
  },
  {
    "path": "tests/Traits/HasPermissionsWithCustomModelsTest.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Support\\Carbon;\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminate\\Support\\Facades\\Event;\nuse Spatie\\Permission\\Contracts\\Permission as PermissionContract;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Events\\PermissionAttachedEvent;\nuse Spatie\\Permission\\Events\\PermissionDetachedEvent;\nuse Spatie\\Permission\\Exceptions\\GuardDoesNotMatch;\nuse Spatie\\Permission\\Exceptions\\PermissionDoesNotExist;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Admin;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Permission;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\SoftDeletingUser;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nbeforeEach(function () {\n    $this->setUpCustomModels();\n    $this->resetDatabaseQuery = config('cache.default') === 'database' ? 1 : 0;\n});\n\n// ---- Tests inherited from HasPermissionsTest ----\n\nit('can assign a permission to a user', function () {\n    $this->testUser->givePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeTrue();\n});\n\nit('can assign a permission to a user with a non default guard', function () {\n    $testUserPermission = app(PermissionContract::class)->create([\n        'name' => 'edit-articles',\n        'guard_name' => 'api',\n    ]);\n\n    $this->testUser->givePermissionTo($testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($testUserPermission))->toBeTrue();\n});\n\nit('throws an exception when assigning a permission that does not exist', function () {\n    expect(fn () => $this->testUser->givePermissionTo('permission-does-not-exist'))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when assigning a permission to a user from a different guard', function () {\n    expect(fn () => $this->testUser->givePermissionTo($this->testAdminPermission))->toThrow(GuardDoesNotMatch::class);\n\n    expect(fn () => $this->testUser->givePermissionTo('admin-permission'))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('can revoke a permission from a user', function () {\n    $this->testUser->givePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeTrue();\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeFalse();\n});\n\nit('can assign and remove a permission using enums', function () {\n    $enum = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::ViewArticles;\n\n    $permission = app(PermissionContract::class)->findOrCreate($enum->value, 'web');\n\n    $this->testUser->givePermissionTo($enum);\n\n    expect($this->testUser->hasPermissionTo($enum))->toBeTrue();\n    expect($this->testUser->hasAnyPermission($enum))->toBeTrue();\n    expect($this->testUser->hasDirectPermission($enum))->toBeTrue();\n\n    $this->testUser->revokePermissionTo($enum);\n\n    expect($this->testUser->hasPermissionTo($enum))->toBeFalse();\n    expect($this->testUser->hasAnyPermission($enum))->toBeFalse();\n    expect($this->testUser->hasDirectPermission($enum))->toBeFalse();\n});\n\nit('can scope users using enums', function () {\n    $enum1 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::ViewArticles;\n    $enum2 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::EditArticles;\n    $permission1 = app(PermissionContract::class)->findOrCreate($enum1->value, 'web');\n    $permission2 = app(PermissionContract::class)->findOrCreate($enum2->value, 'web');\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo([$enum1, $enum2]);\n    $this->testUserRole->givePermissionTo($enum2);\n    $user2->assignRole('testRole');\n\n    $scopedUsers1 = User::permission($enum2)->get();\n    $scopedUsers2 = User::permission([$enum1])->get();\n    $scopedUsers3 = User::withoutPermission([$enum1])->get();\n    $scopedUsers4 = User::withoutPermission([$enum2])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n    expect($scopedUsers4->count())->toEqual(1);\n});\n\nit('can scope users using a string', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n\n    $scopedUsers1 = User::permission('edit-articles')->get();\n    $scopedUsers2 = User::permission(['edit-news'])->get();\n    $scopedUsers3 = User::withoutPermission('edit-news')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using an array', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::permission(['edit-articles', 'edit-news'])->get();\n    $scopedUsers2 = User::permission(['edit-news'])->get();\n    $scopedUsers3 = User::withoutPermission(['edit-news'])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using a collection', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::permission(collect(['edit-articles', 'edit-news']))->get();\n    $scopedUsers2 = User::permission(collect(['edit-news']))->get();\n    $scopedUsers3 = User::withoutPermission(collect(['edit-news']))->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using an object', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user1->givePermissionTo($this->testUserPermission->name);\n\n    $scopedUsers1 = User::permission($this->testUserPermission)->get();\n    $scopedUsers2 = User::permission([$this->testUserPermission])->get();\n    $scopedUsers3 = User::permission(collect([$this->testUserPermission]))->get();\n    $scopedUsers4 = User::withoutPermission(collect([$this->testUserPermission]))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(1);\n    expect($scopedUsers4->count())->toEqual(0);\n});\n\nit('can scope users without direct permissions only role', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::permission('edit-articles')->get();\n    $scopedUsers2 = User::withoutPermission('edit-articles')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('can scope users with only direct permission', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-news']);\n    $user2->givePermissionTo(['edit-articles', 'edit-news']);\n\n    $scopedUsers1 = User::permission('edit-news')->get();\n    $scopedUsers2 = User::withoutPermission('edit-news')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('throws an exception when calling hasPermissionTo with an invalid type', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasPermissionTo(new \\stdClass))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when calling hasPermissionTo with null', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasPermissionTo(null))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when calling hasDirectPermission with an invalid type', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasDirectPermission(new \\stdClass))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when calling hasDirectPermission with null', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasDirectPermission(null))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when trying to scope a non existing permission', function () {\n    expect(fn () => User::permission('not defined permission')->get())->toThrow(PermissionDoesNotExist::class);\n\n    expect(fn () => User::withoutPermission('not defined permission')->get())->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when trying to scope a permission from another guard', function () {\n    expect(fn () => User::permission('testAdminPermission')->get())->toThrow(PermissionDoesNotExist::class);\n\n    expect(fn () => User::withoutPermission('testAdminPermission')->get())->toThrow(PermissionDoesNotExist::class);\n});\n\nit('doesnt detach permissions when user soft deleting', function () {\n    $user = SoftDeletingUser::create(['email' => 'test@example.com']);\n    $user->givePermissionTo(['edit-news']);\n    $user->delete();\n\n    $user = SoftDeletingUser::withTrashed()->find($user->id);\n\n    expect($user->hasPermissionTo('edit-news'))->toBeTrue();\n});\n\nit('can give and revoke multiple permissions', function () {\n    $this->testUserRole->givePermissionTo(['edit-articles', 'edit-news']);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(2);\n\n    $this->testUserRole->revokePermissionTo(['edit-articles', 'edit-news']);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(0);\n});\n\nit('can give and revoke permissions models array', function () {\n    $models = [app(PermissionContract::class)::where('name', 'edit-articles')->first(), app(PermissionContract::class)::where('name', 'edit-news')->first()];\n\n    $this->testUserRole->givePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(2);\n\n    $this->testUserRole->revokePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(0);\n});\n\nit('can give and revoke permissions models collection', function () {\n    $models = app(PermissionContract::class)::whereIn('name', ['edit-articles', 'edit-news'])->get();\n\n    $this->testUserRole->givePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(2);\n\n    $this->testUserRole->revokePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(0);\n});\n\nit('can determine that the user does not have a permission', function () {\n    expect($this->testUser->hasPermissionTo('edit-articles'))->toBeFalse();\n});\n\nit('throws an exception when the permission does not exist', function () {\n    expect(fn () => $this->testUser->hasPermissionTo('does-not-exist'))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when the permission does not exist for this guard', function () {\n    expect(fn () => $this->testUser->hasPermissionTo('does-not-exist', 'web'))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('can reject a user that does not have any permissions at all', function () {\n    $user = new User;\n\n    expect($user->hasPermissionTo('edit-articles'))->toBeFalse();\n});\n\nit('can determine that the user has any of the permissions directly', function () {\n    expect($this->testUser->hasAnyPermission('edit-articles'))->toBeFalse();\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAnyPermission('edit-news', 'edit-articles'))->toBeTrue();\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasAnyPermission('edit-articles', 'edit-news'))->toBeTrue();\n    expect($this->testUser->hasAnyPermission('edit-blog', 'Edit News', ['Edit News']))->toBeFalse();\n});\n\nit('can determine that the user has any of the permissions directly using an array', function () {\n    expect($this->testUser->hasAnyPermission(['edit-articles']))->toBeFalse();\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAnyPermission(['edit-news', 'edit-articles']))->toBeTrue();\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasAnyPermission(['edit-articles', 'edit-news']))->toBeTrue();\n});\n\nit('can determine that the user has any of the permissions via role', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAnyPermission('edit-news', 'edit-articles'))->toBeTrue();\n    expect($this->testUser->hasAnyPermission('edit-blog', 'Edit News', ['Edit News']))->toBeFalse();\n});\n\nit('can determine that the user has all of the permissions directly', function () {\n    $this->testUser->givePermissionTo('edit-articles', 'edit-news');\n\n    expect($this->testUser->hasAllPermissions('edit-articles', 'edit-news'))->toBeTrue();\n\n    $this->testUser->revokePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAllPermissions('edit-articles', 'edit-news'))->toBeFalse();\n    expect($this->testUser->hasAllPermissions(['edit-articles', 'edit-news'], 'edit-blog'))->toBeFalse();\n});\n\nit('can determine that the user has all of the permissions directly using an array', function () {\n    expect($this->testUser->hasAllPermissions(['edit-articles', 'edit-news']))->toBeFalse();\n\n    $this->testUser->revokePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAllPermissions(['edit-news', 'edit-articles']))->toBeFalse();\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasAllPermissions(['edit-articles', 'edit-news']))->toBeFalse();\n});\n\nit('can determine that the user has all of the permissions via role', function () {\n    $this->testUserRole->givePermissionTo('edit-articles', 'edit-news');\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAllPermissions('edit-articles', 'edit-news'))->toBeTrue();\n});\n\nit('can determine that user has direct permission', function () {\n    $this->testUser->givePermissionTo('edit-articles');\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->getDirectPermissions()->pluck('name'))->toEqual(collect(['edit-articles']));\n\n    $this->testUser->revokePermissionTo('edit-articles');\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeFalse();\n\n    $this->testUser->assignRole('testRole');\n    $this->testUserRole->givePermissionTo('edit-articles');\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeFalse();\n});\n\nit('can list all the permissions via roles of user', function () {\n    $roleModel = app(Role::class);\n    $roleModel->findByName('testRole2')->givePermissionTo('edit-news');\n\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    expect($this->testUser->getPermissionsViaRoles()->pluck('name')->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n});\n\nit('can list all the coupled permissions both directly and via roles', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->getAllPermissions()->pluck('name')->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n});\n\nit('can sync multiple permissions', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->syncPermissions('edit-articles', 'edit-blog');\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-news'))->toBeFalse();\n});\n\nit('can avoid sync duplicated permissions', function () {\n    $this->testUser->syncPermissions('edit-articles', 'edit-blog', 'edit-blog');\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n});\n\nit('can avoid detach on permission that does not exist sync', function () {\n    $this->testUser->syncPermissions('edit-articles');\n\n    expect(fn () => $this->testUser->syncPermissions('permission-does-not-exist'))->toThrow(PermissionDoesNotExist::class);\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->checkPermissionTo('permission-does-not-exist'))->toBeFalse();\n});\n\nit('can sync multiple permissions by id', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $ids = app(PermissionContract::class)::whereIn('name', ['edit-articles', 'edit-blog'])->pluck($this->testUserPermission->getKeyName());\n\n    $this->testUser->syncPermissions($ids);\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-news'))->toBeFalse();\n});\n\nit('sync permission ignores null inputs', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $ids = app(PermissionContract::class)::whereIn('name', ['edit-articles', 'edit-blog'])->pluck($this->testUserPermission->getKeyName());\n\n    $ids->push(null);\n\n    $this->testUser->syncPermissions($ids);\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-news'))->toBeFalse();\n});\n\nit('sync permission error does not detach permissions', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    expect(fn () => $this->testUser->syncPermissions('edit-articles', 'permission-that-does-not-exist'))->toThrow(PermissionDoesNotExist::class);\n\n    expect($this->testUser->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('does not remove already associated permissions when assigning new permissions', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('does not throw an exception when assigning a permission that is already assigned', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    expect($this->testUser->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('can sync permissions to a model that is not persisted', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncPermissions('edit-articles');\n    $user->save();\n    $user->save(); // test save same model twice\n\n    expect($user->hasPermissionTo('edit-articles'))->toBeTrue();\n\n    $user->syncPermissions('edit-articles');\n    expect($user->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($user->fresh()->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n\nit('does not run unnecessary sqls when assigning new permissions', function () {\n    $permission2 = app(PermissionContract::class)->where('name', ['edit-news'])->first();\n\n    DB::enableQueryLog();\n    $this->testUser->syncPermissions($this->testUserPermission, $permission2);\n    DB::disableQueryLog();\n\n    $necessaryQueriesCount = 2;\n\n    expect(DB::getQueryLog())->toHaveCount($necessaryQueriesCount);\n});\n\nit('calling givePermissionTo before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->givePermissionTo('edit-news');\n    $user->save();\n\n    $user2 = new User(['email' => 'test2@user.com']);\n    $user2->givePermissionTo('edit-articles');\n\n    DB::enableQueryLog();\n    $user2->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasPermissionTo('edit-news'))->toBeTrue();\n    expect($user->fresh()->hasPermissionTo('edit-articles'))->toBeFalse();\n\n    expect($user2->fresh()->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($user2->fresh()->hasPermissionTo('edit-news'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('calling syncPermissions before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncPermissions('edit-news');\n    $user->save();\n\n    $user2 = new User(['email' => 'test2@user.com']);\n    $user2->syncPermissions('edit-articles');\n\n    DB::enableQueryLog();\n    $user2->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasPermissionTo('edit-news'))->toBeTrue();\n    expect($user->fresh()->hasPermissionTo('edit-articles'))->toBeFalse();\n\n    expect($user2->fresh()->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($user2->fresh()->hasPermissionTo('edit-news'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('can retrieve permission names', function () {\n    $this->testUser->givePermissionTo('edit-news', 'edit-articles');\n    expect($this->testUser->getPermissionNames()->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n});\n\nit('can check many direct permissions', function () {\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n    expect($this->testUser->hasAllDirectPermissions(['edit-news', 'edit-articles']))->toBeTrue();\n    expect($this->testUser->hasAllDirectPermissions('edit-news', 'edit-articles'))->toBeTrue();\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-news', 'edit-blog']))->toBeFalse();\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-news'], 'edit-blog'))->toBeFalse();\n});\n\nit('can check if there is any of the direct permissions given', function () {\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n    expect($this->testUser->hasAnyDirectPermission(['edit-news', 'edit-blog']))->toBeTrue();\n    expect($this->testUser->hasAnyDirectPermission('edit-news', 'edit-blog'))->toBeTrue();\n    expect($this->testUser->hasAnyDirectPermission('edit-blog', 'Edit News', ['Edit News']))->toBeFalse();\n});\n\nit('can check permission based on logged in user guard', function () {\n    $this->testUser->givePermissionTo(app(PermissionContract::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'api',\n    ]));\n    $response = $this->actingAs($this->testUser, 'api')\n        ->json('GET', '/check-api-guard-permission');\n    $response->assertJson([\n        'status' => true,\n    ]);\n});\n\nit('can reject permission based on logged in user guard', function () {\n    $unassignedPermission = app(PermissionContract::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'api',\n    ]);\n\n    $assignedPermission = app(PermissionContract::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'web',\n    ]);\n\n    $this->testUser->givePermissionTo($assignedPermission);\n    $response = $this->withExceptionHandling()\n        ->actingAs($this->testUser, 'api')\n        ->json('GET', '/check-api-guard-permission');\n    $response->assertJson([\n        'status' => false,\n    ]);\n});\n\nit('fires an event when a permission is added', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n\n    $ids = app(PermissionContract::class)::whereIn('name', ['edit-articles', 'edit-news'])\n        ->pluck($this->testUserPermission->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(PermissionAttachedEvent::class, function ($event) use ($ids) {\n        return $event->model instanceof User\n            && $event->model->hasPermissionTo('edit-news')\n            && $event->model->hasPermissionTo('edit-articles')\n            && $ids === $event->permissionsOrIds;\n    });\n});\n\nit('does not fire an event when events are not enabled', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', false);\n\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n\n    $ids = app(PermissionContract::class)::whereIn('name', ['edit-articles', 'edit-news'])\n        ->pluck($this->testUserPermission->getKeyName())\n        ->toArray();\n\n    Event::assertNotDispatched(PermissionAttachedEvent::class);\n});\n\nit('fires an event when a permission is removed', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $permissions = app(PermissionContract::class)::whereIn('name', ['edit-articles', 'edit-news'])->get();\n\n    $this->testUser->givePermissionTo($permissions);\n\n    $this->testUser->revokePermissionTo($permissions);\n\n    Event::assertDispatched(PermissionDetachedEvent::class, function ($event) use ($permissions) {\n        return $event->model instanceof User\n            && ! $event->model->hasPermissionTo('edit-news')\n            && ! $event->model->hasPermissionTo('edit-articles')\n            && $event->permissionsOrIds === $permissions;\n    });\n});\n\nit('can be given a permission on role when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    $testRole = app(Role::class)->with('permissions')->get()->first();\n\n    $testRole->givePermissionTo('edit-articles');\n\n    expect($testRole->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n\nit('can be given a permission on user when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    User::create(['email' => 'other@user.com']);\n    $testUser = User::with('permissions')->get()->first();\n\n    $testUser->givePermissionTo('edit-articles');\n\n    expect($testUser->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n\n// ---- Custom model-specific tests ----\n\nit('can use custom model permission', function () {\n    expect(get_class($this->testUserPermission))->toBe(Permission::class);\n});\n\nit('can use custom fields from cache', function () {\n    DB::connection()->getSchemaBuilder()->table(config('permission.table_names.roles'), function ($table) {\n        $table->string('type')->default('R');\n    });\n    DB::connection()->getSchemaBuilder()->table(config('permission.table_names.permissions'), function ($table) {\n        $table->string('type')->default('P');\n    });\n\n    $this->testUserRole->givePermissionTo($this->testUserPermission);\n    app(PermissionRegistrar::class)->getPermissions();\n\n    DB::enableQueryLog();\n    expect(Permission::findByName('edit-articles')->type)->toBe('P');\n    expect(Permission::findByName('edit-articles')->roles[0]->type)->toBe('R');\n    DB::disableQueryLog();\n\n    expect(count(DB::getQueryLog()))->toBe(0);\n});\n\nit('can scope users using a int', function () {\n    // Skipped because custom model uses uuid,\n    // replacement \"it_can_scope_users_using_a_uuid\"\n    expect(true)->toBeTrue();\n});\n\nit('can scope users using a uuid', function () {\n    $uuid1 = $this->testUserPermission->getKey();\n    $uuid2 = app(Permission::class)::where('name', 'edit-news')->first()->getKey();\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->givePermissionTo([$uuid1, $uuid2]);\n    $this->testUserRole->givePermissionTo($uuid1);\n    $user2->assignRole('testRole');\n\n    $scopedUsers1 = User::permission($uuid1)->get();\n    $scopedUsers2 = User::permission([$uuid2])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('doesnt detach roles when soft deleting', function () {\n    $this->testUserRole->givePermissionTo($this->testUserPermission);\n\n    DB::enableQueryLog();\n    $this->testUserPermission->delete();\n    DB::disableQueryLog();\n\n    expect(count(DB::getQueryLog()))->toBe(1 + $this->resetDatabaseQuery);\n\n    $permission = Permission::onlyTrashed()->find($this->testUserPermission->getKey());\n\n    expect(DB::table(config('permission.table_names.role_has_permissions'))->where('permission_test_id', $permission->getKey())->count())->toEqual(1);\n});\n\nit('doesnt detach users when soft deleting', function () {\n    $this->testUser->givePermissionTo($this->testUserPermission);\n\n    DB::enableQueryLog();\n    $this->testUserPermission->delete();\n    DB::disableQueryLog();\n\n    expect(count(DB::getQueryLog()))->toBe(1 + $this->resetDatabaseQuery);\n\n    $permission = Permission::onlyTrashed()->find($this->testUserPermission->getKey());\n\n    expect(DB::table(config('permission.table_names.model_has_permissions'))->where('permission_test_id', $permission->getKey())->count())->toEqual(1);\n});\n\nit('does detach roles and users when force deleting', function () {\n    $permission_id = $this->testUserPermission->getKey();\n    $this->testUserRole->givePermissionTo($permission_id);\n    $this->testUser->givePermissionTo($permission_id);\n\n    DB::enableQueryLog();\n    $this->testUserPermission->forceDelete();\n    DB::disableQueryLog();\n\n    expect(count(DB::getQueryLog()))->toBe(3 + $this->resetDatabaseQuery); // avoid detach permissions on permissions\n\n    $permission = Permission::withTrashed()->find($permission_id);\n\n    expect($permission)->toBeNull();\n    expect(DB::table(config('permission.table_names.role_has_permissions'))->where('permission_test_id', $permission_id)->count())->toEqual(0);\n    expect(DB::table(config('permission.table_names.model_has_permissions'))->where('permission_test_id', $permission_id)->count())->toEqual(0);\n});\n\nit('should touch when assigning new permissions', function () {\n    Carbon::setTestNow('2021-07-19 10:13:14');\n\n    $user = Admin::create(['email' => 'user1@test.com']);\n    $permission1 = Permission::create(['name' => 'edit-news', 'guard_name' => 'admin']);\n    $permission2 = Permission::create(['name' => 'edit-blog', 'guard_name' => 'admin']);\n\n    expect($permission1->updated_at->format('Y-m-d H:i:s'))->toBe('2021-07-19 10:13:14');\n\n    Carbon::setTestNow('2021-07-20 19:13:14');\n\n    $user->syncPermissions([$permission1->getKey(), $permission2->getKey()]);\n\n    expect($permission1->refresh()->updated_at->format('Y-m-d H:i:s'))->toBe('2021-07-20 19:13:14');\n    expect($permission2->refresh()->updated_at->format('Y-m-d H:i:s'))->toBe('2021-07-20 19:13:14');\n});\n"
  },
  {
    "path": "tests/Traits/HasRolesTest.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminate\\Support\\Facades\\Event;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Events\\RoleAttachedEvent;\nuse Spatie\\Permission\\Events\\RoleDetachedEvent;\nuse Spatie\\Permission\\Exceptions\\GuardDoesNotMatch;\nuse Spatie\\Permission\\Exceptions\\RoleDoesNotExist;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Admin;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\SoftDeletingUser;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nit('can determine that the user does not have a role', function () {\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    $role = app(Role::class)->findOrCreate('testRoleInWebGuard', 'web');\n\n    expect($this->testUser->hasRole($role))->toBeFalse();\n\n    $this->testUser->assignRole($role);\n    expect($this->testUser->hasRole($role))->toBeTrue();\n    expect($this->testUser->hasRole($role->name))->toBeTrue();\n    expect($this->testUser->hasRole($role->name, $role->guard_name))->toBeTrue();\n    expect($this->testUser->hasRole([$role->name, 'fakeRole'], $role->guard_name))->toBeTrue();\n    expect($this->testUser->hasRole($role->getKey(), $role->guard_name))->toBeTrue();\n    expect($this->testUser->hasRole([$role->getKey(), 'fakeRole'], $role->guard_name))->toBeTrue();\n\n    expect($this->testUser->hasRole($role->name, 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasRole([$role->name, 'fakeRole'], 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasRole($role->getKey(), 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasRole([$role->getKey(), 'fakeRole'], 'fakeGuard'))->toBeFalse();\n\n    $role = app(Role::class)->findOrCreate('testRoleInWebGuard2', 'web');\n    expect($this->testUser->hasRole($role))->toBeFalse();\n});\n\nit('can assign and remove a role using enums', function () {\n    $enum1 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::UserManager;\n    $enum2 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::Writer;\n    $enum3 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::CastedEnum1;\n    $enum4 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::CastedEnum2;\n\n    app(Role::class)->findOrCreate($enum1->value, 'web');\n    app(Role::class)->findOrCreate($enum2->value, 'web');\n    app(Role::class)->findOrCreate($enum3->value, 'web');\n    app(Role::class)->findOrCreate($enum4->value, 'web');\n\n    expect($this->testUser->hasRole($enum1))->toBeFalse();\n    expect($this->testUser->hasRole($enum2))->toBeFalse();\n    expect($this->testUser->hasRole($enum3))->toBeFalse();\n    expect($this->testUser->hasRole($enum4))->toBeFalse();\n    expect($this->testUser->hasRole('user-manager'))->toBeFalse();\n    expect($this->testUser->hasRole('writer'))->toBeFalse();\n    expect($this->testUser->hasRole('casted_enum-1'))->toBeFalse();\n    expect($this->testUser->hasRole('casted_enum-2'))->toBeFalse();\n\n    $this->testUser->assignRole($enum1);\n    $this->testUser->assignRole($enum2);\n    $this->testUser->assignRole($enum3);\n    $this->testUser->assignRole($enum4);\n\n    expect($this->testUser->hasRole($enum1))->toBeTrue();\n    expect($this->testUser->hasRole($enum2))->toBeTrue();\n    expect($this->testUser->hasRole($enum3))->toBeTrue();\n    expect($this->testUser->hasRole($enum4))->toBeTrue();\n\n    expect($this->testUser->hasRole([$enum1, 'writer']))->toBeTrue();\n    expect($this->testUser->hasRole([$enum3, 'casted_enum-2']))->toBeTrue();\n\n    expect($this->testUser->hasAllRoles([$enum1, $enum2, $enum3, $enum4]))->toBeTrue();\n    expect($this->testUser->hasAllRoles(['user-manager', 'writer', 'casted_enum-1', 'casted_enum-2']))->toBeTrue();\n    expect($this->testUser->hasAllRoles([$enum1, $enum2, $enum3, $enum4, 'not exist']))->toBeFalse();\n    expect($this->testUser->hasAllRoles(['user-manager', 'writer', 'casted_enum-1', 'casted_enum-2', 'not exist']))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles([$enum4, $enum3, $enum2, $enum1]))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['user-manager', 'writer', 'casted_enum-1', 'casted_enum-2']))->toBeTrue();\n\n    $this->testUser->removeRole($enum1);\n\n    expect($this->testUser->hasRole($enum1))->toBeFalse();\n});\n\nit('can scope a role using enums', function () {\n    $enum1 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::UserManager;\n    $enum2 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::Writer;\n    $role1 = app(Role::class)->findOrCreate($enum1->value, 'web');\n    $role2 = app(Role::class)->findOrCreate($enum2->value, 'web');\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n\n    // assign only one user to a role\n    $user2->assignRole($enum1);\n    expect($user2->hasRole($enum1))->toBeTrue();\n    expect($user2->hasRole($enum2))->toBeFalse();\n\n    $scopedUsers1 = User::role($enum1)->get();\n    $scopedUsers2 = User::role($enum2)->get();\n    $scopedUsers3 = User::withoutRole($enum2)->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(0);\n    expect($scopedUsers3->count())->toEqual(3);\n});\n\nit('can assign and remove a role', function () {\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    $this->testUser->removeRole('testRole');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n});\n\nit('removes a role and returns roles', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->assignRole('testRole2');\n\n    expect($this->testUser->hasRole(['testRole', 'testRole2']))->toBeTrue();\n\n    $roles = $this->testUser->removeRole('testRole');\n\n    expect($roles->hasRole('testRole'))->toBeFalse();\n\n    expect($roles->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can assign and remove a role on a permission', function () {\n    $this->testUserPermission->assignRole('testRole');\n\n    expect($this->testUserPermission->hasRole('testRole'))->toBeTrue();\n\n    $this->testUserPermission->removeRole('testRole');\n\n    expect($this->testUserPermission->hasRole('testRole'))->toBeFalse();\n});\n\nit('can assign and remove a role using an object', function () {\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeTrue();\n\n    $this->testUser->removeRole($this->testUserRole);\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeFalse();\n});\n\nit('can assign and remove a role using an id', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeTrue();\n\n    $this->testUser->removeRole($this->testUserRole->getKey());\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeFalse();\n});\n\nit('can assign and remove multiple roles at once', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey(), 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n\n    $this->testUser->removeRole($this->testUserRole->getKey(), 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n});\n\nit('can assign and remove multiple roles using an array', function () {\n    $this->testUser->assignRole([$this->testUserRole->getKey(), 'testRole2']);\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n\n    $this->testUser->removeRole([$this->testUserRole->getKey(), 'testRole2']);\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n});\n\nit('does not remove already associated roles when assigning new roles', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    $this->testUser->assignRole('testRole2');\n\n    expect($this->testUser->fresh()->hasRole('testRole'))->toBeTrue();\n});\n\nit('does not throw an exception when assigning a role that is already assigned', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    expect($this->testUser->fresh()->hasRole('testRole'))->toBeTrue();\n});\n\nit('throws an exception when assigning a role that does not exist', function () {\n    expect(fn () => $this->testUser->assignRole('evil-emperor'))->toThrow(RoleDoesNotExist::class);\n});\n\nit('can only assign roles from the correct guard', function () {\n    expect(fn () => $this->testUser->assignRole('testAdminRole'))->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when assigning a role from a different guard', function () {\n    expect(fn () => $this->testUser->assignRole($this->testAdminRole))->toThrow(GuardDoesNotMatch::class);\n});\n\nit('ignores null roles when syncing', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->syncRoles('testRole2', null);\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can sync roles from a string', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->syncRoles('testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can sync roles from a string on a permission', function () {\n    $this->testUserPermission->assignRole('testRole');\n\n    $this->testUserPermission->syncRoles('testRole2');\n\n    expect($this->testUserPermission->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUserPermission->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can avoid sync duplicated roles', function () {\n    $this->testUser->syncRoles('testRole', 'testRole', 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can avoid detach on role that does not exist sync', function () {\n    $this->testUser->syncRoles('testRole');\n\n    expect(fn () => $this->testUser->syncRoles('role-does-not-exist'))->toThrow(RoleDoesNotExist::class);\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n    expect($this->testUser->hasRole('role-does-not-exist'))->toBeFalse();\n});\n\nit('can sync multiple roles', function () {\n    $this->testUser->syncRoles('testRole', 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can sync multiple roles from an array', function () {\n    $this->testUser->syncRoles(['testRole', 'testRole2']);\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('will remove all roles when an empty array is passed to sync roles', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->assignRole('testRole2');\n\n    $this->testUser->syncRoles([]);\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n});\n\nit('sync roles error does not detach roles', function () {\n    $this->testUser->assignRole('testRole');\n\n    expect(fn () => $this->testUser->syncRoles('testRole2', 'role-that-does-not-exist'))->toThrow(RoleDoesNotExist::class);\n\n    expect($this->testUser->fresh()->hasRole('testRole'))->toBeTrue();\n});\n\nit('will sync roles to a model that is not persisted', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncRoles([$this->testUserRole]);\n    $user->save();\n    $user->save(); // test save same model twice\n\n    expect($user->hasRole($this->testUserRole))->toBeTrue();\n\n    $user->syncRoles([$this->testUserRole]);\n    expect($user->hasRole($this->testUserRole))->toBeTrue();\n    expect($user->fresh()->hasRole($this->testUserRole))->toBeTrue();\n});\n\nit('does not run unnecessary sqls when assigning new roles', function () {\n    $role2 = app(Role::class)->where('name', ['testRole2'])->first();\n\n    DB::enableQueryLog();\n    $this->testUser->syncRoles($this->testUserRole, $role2);\n    DB::disableQueryLog();\n\n    $necessaryQueriesCount = 2;\n\n    // Teams reloads relation, adding an extra query\n    if (app(PermissionRegistrar::class)->teams) {\n        $necessaryQueriesCount++;\n    }\n\n    expect(DB::getQueryLog())->toHaveCount($necessaryQueriesCount);\n});\n\nit('calling syncRoles before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncRoles('testRole');\n    $user->save();\n\n    $user2 = new User(['email' => 'admin@user.com']);\n    $user2->syncRoles('testRole2');\n\n    DB::enableQueryLog();\n    $user2->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasRole('testRole'))->toBeTrue();\n    expect($user->fresh()->hasRole('testRole2'))->toBeFalse();\n\n    expect($user2->fresh()->hasRole('testRole2'))->toBeTrue();\n    expect($user2->fresh()->hasRole('testRole'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('calling assignRole before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->assignRole('testRole');\n    $user->save();\n\n    $admin_user = new User(['email' => 'admin@user.com']);\n    $admin_user->assignRole('testRole2');\n\n    DB::enableQueryLog();\n    $admin_user->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasRole('testRole'))->toBeTrue();\n    expect($user->fresh()->hasRole('testRole2'))->toBeFalse();\n\n    expect($admin_user->fresh()->hasRole('testRole2'))->toBeTrue();\n    expect($admin_user->fresh()->hasRole('testRole'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('throws an exception when syncing a role from another guard', function () {\n    expect(fn () => $this->testUser->syncRoles('testRole', 'testAdminRole'))->toThrow(RoleDoesNotExist::class);\n\n    expect(fn () => $this->testUser->syncRoles('testRole', $this->testAdminRole))->toThrow(GuardDoesNotMatch::class);\n});\n\nit('deletes pivot table entries when deleting models', function () {\n    $user = User::create(['email' => 'user@test.com']);\n\n    $user->assignRole('testRole');\n    $user->givePermissionTo('edit-articles');\n\n    $this->assertDatabaseHas('model_has_permissions', [config('permission.column_names.model_morph_key') => $user->id]);\n    $this->assertDatabaseHas('model_has_roles', [config('permission.column_names.model_morph_key') => $user->id]);\n\n    $user->delete();\n\n    $this->assertDatabaseMissing('model_has_permissions', [config('permission.column_names.model_morph_key') => $user->id]);\n    $this->assertDatabaseMissing('model_has_roles', [config('permission.column_names.model_morph_key') => $user->id]);\n});\n\nit('can scope users using a string', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n\n    $scopedUsers = User::role('testRole')->get();\n\n    expect($scopedUsers->count())->toEqual(1);\n});\n\nit('can withoutscope users using a string', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers = User::withoutRole('testRole2')->get();\n\n    expect($scopedUsers->count())->toEqual(1);\n});\n\nit('can scope users using an array', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role([$this->testUserRole])->get();\n    $scopedUsers2 = User::role(['testRole', 'testRole2'])->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(2);\n});\n\nit('can withoutscope users using an array', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole([$this->testUserRole])->get();\n    $scopedUsers2 = User::withoutRole([$this->testUserRole->name, 'testRole2'])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(0);\n});\n\nit('can scope users using an array of ids and names', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $firstAssignedRoleName = $this->testUserRole->name;\n    $secondAssignedRoleId = app(Role::class)->findByName('testRole2')->getKey();\n\n    $scopedUsers = User::role([$firstAssignedRoleName, $secondAssignedRoleId])->get();\n\n    expect($scopedUsers->count())->toEqual(2);\n});\n\nit('can withoutscope users using an array of ids and names', function () {\n    app(Role::class)->create(['name' => 'testRole3']);\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $firstAssignedRoleName = $this->testUserRole->name;\n    $unassignedRoleId = app(Role::class)->findByName('testRole3')->getKey();\n\n    $scopedUsers = User::withoutRole([$firstAssignedRoleName, $unassignedRoleId])->get();\n\n    expect($scopedUsers->count())->toEqual(2);\n});\n\nit('can scope users using a collection', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role([$this->testUserRole])->get();\n    $scopedUsers2 = User::role(collect(['testRole', 'testRole2']))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(2);\n});\n\nit('can withoutscope users using a collection', function () {\n    app(Role::class)->create(['name' => 'testRole3']);\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole([$this->testUserRole])->get();\n    $scopedUsers2 = User::withoutRole(collect(['testRole', 'testRole3']))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('can scope users using an object', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role($this->testUserRole)->get();\n    $scopedUsers2 = User::role([$this->testUserRole])->get();\n    $scopedUsers3 = User::role(collect([$this->testUserRole]))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(1);\n});\n\nit('can withoutscope users using an object', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole($this->testUserRole)->get();\n    $scopedUsers2 = User::withoutRole([$this->testUserRole])->get();\n    $scopedUsers3 = User::withoutRole(collect([$this->testUserRole]))->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(2);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope against a specific guard', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role('testRole', 'web')->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n\n    $user3 = Admin::create(['email' => 'user3@test.com']);\n    $user4 = Admin::create(['email' => 'user4@test.com']);\n    $user5 = Admin::create(['email' => 'user5@test.com']);\n    $testAdminRole2 = app(Role::class)->create(['name' => 'testAdminRole2', 'guard_name' => 'admin']);\n    $user3->assignRole($this->testAdminRole);\n    $user4->assignRole($this->testAdminRole);\n    $user5->assignRole($testAdminRole2);\n    $scopedUsers2 = Admin::role('testAdminRole', 'admin')->get();\n    $scopedUsers3 = Admin::role('testAdminRole2', 'admin')->get();\n\n    expect($scopedUsers2->count())->toEqual(2);\n    expect($scopedUsers3->count())->toEqual(1);\n});\n\nit('can withoutscope against a specific guard', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole('testRole', 'web')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n\n    Admin::all()->each(fn ($item) => $item->delete());\n    $user4 = Admin::create(['email' => 'user4@test.com']);\n    $user5 = Admin::create(['email' => 'user5@test.com']);\n    $user6 = Admin::create(['email' => 'user6@test.com']);\n    $testAdminRole2 = app(Role::class)->create(['name' => 'testAdminRole2', 'guard_name' => 'admin']);\n    $user4->assignRole($this->testAdminRole);\n    $user5->assignRole($this->testAdminRole);\n    $user6->assignRole($testAdminRole2);\n    $scopedUsers2 = Admin::withoutRole('testAdminRole', 'admin')->get();\n    $scopedUsers3 = Admin::withoutRole('testAdminRole2', 'admin')->get();\n\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('throws an exception when trying to scope a role from another guard', function () {\n    expect(fn () => User::role('testAdminRole')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when trying to call withoutscope on a role from another guard', function () {\n    expect(fn () => User::withoutRole('testAdminRole')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when trying to scope a non existing role', function () {\n    expect(fn () => User::role('role not defined')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when trying to use withoutscope on a non existing role', function () {\n    expect(fn () => User::withoutRole('role not defined')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('can determine that a user has one of the given roles', function () {\n    $roleModel = app(Role::class);\n\n    $roleModel->create(['name' => 'second role']);\n\n    expect($this->testUser->hasRole($roleModel->all()))->toBeFalse();\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasRole($roleModel->all()))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole($roleModel->all()))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole('role does not exist'))->toBeFalse();\n\n    expect($this->testUser->hasAnyRole(['testRole']))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole(['testRole', 'role does not exist']))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole(['role does not exist']))->toBeFalse();\n\n    expect($this->testUser->hasAnyRole('testRole', 'role does not exist'))->toBeTrue();\n});\n\nit('can determine that a user has all of the given roles', function () {\n    $roleModel = app(Role::class);\n\n    expect($this->testUser->hasAllRoles($roleModel->first()))->toBeFalse();\n\n    expect($this->testUser->hasAllRoles('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasAllRoles($roleModel->all()))->toBeFalse();\n\n    $roleModel->create(['name' => 'second role']);\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasAllRoles('testRole'))->toBeTrue();\n    expect($this->testUser->hasAllRoles('testRole', 'web'))->toBeTrue();\n    expect($this->testUser->hasAllRoles('testRole', 'fakeGuard'))->toBeFalse();\n\n    expect($this->testUser->hasAllRoles(['testRole', 'second role']))->toBeFalse();\n    expect($this->testUser->hasAllRoles(['testRole', 'second role'], 'web'))->toBeFalse();\n\n    $this->testUser->assignRole('second role');\n\n    expect($this->testUser->hasAllRoles(['testRole', 'second role']))->toBeTrue();\n    expect($this->testUser->hasAllRoles(['testRole', 'second role'], 'web'))->toBeTrue();\n    expect($this->testUser->hasAllRoles(['testRole', 'second role'], 'fakeGuard'))->toBeFalse();\n});\n\nit('can determine that a user has exact all of the given roles', function () {\n    $roleModel = app(Role::class);\n\n    expect($this->testUser->hasExactRoles($roleModel->first()))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles($roleModel->all()))->toBeFalse();\n\n    $roleModel->create(['name' => 'second role']);\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasExactRoles('testRole'))->toBeTrue();\n    expect($this->testUser->hasExactRoles('testRole', 'web'))->toBeTrue();\n    expect($this->testUser->hasExactRoles('testRole', 'fakeGuard'))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles(['testRole', 'second role']))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'web'))->toBeFalse();\n\n    $this->testUser->assignRole('second role');\n\n    expect($this->testUser->hasExactRoles(['testRole', 'second role']))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'web'))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'fakeGuard'))->toBeFalse();\n\n    $roleModel->create(['name' => 'third role']);\n    $this->testUser->assignRole('third role');\n\n    expect($this->testUser->hasExactRoles(['testRole', 'second role']))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'web'))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role', 'third role']))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role', 'third role'], 'web'))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role', 'third role'], 'fakeGuard'))->toBeFalse();\n});\n\nit('can determine that a user does not have a role from another guard', function () {\n    expect($this->testUser->hasRole('testAdminRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole($this->testAdminRole))->toBeFalse();\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAnyRole(['testRole', 'testAdminRole']))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole('testAdminRole', $this->testAdminRole))->toBeFalse();\n});\n\nit('can check against any multiple roles using multiple arguments', function () {\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAnyRole($this->testAdminRole, ['testRole'], 'This Role Does Not Even Exist'))->toBeTrue();\n});\n\nit('returns false instead of an exception when checking against any undefined roles using multiple arguments', function () {\n    expect($this->testUser->hasAnyRole('This Role Does Not Even Exist', $this->testAdminRole))->toBeFalse();\n});\n\nit('throws an exception if an unsupported type is passed to hasRoles', function () {\n    expect(fn () => $this->testUser->hasRole(new class {}))->toThrow(\\TypeError::class);\n});\n\nit('can retrieve role names', function () {\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    expect($this->testUser->getRoleNames()->sort()->values())->toEqual(\n        collect(['testRole', 'testRole2'])\n    );\n});\n\nit('does not detach roles when user soft deleting', function () {\n    $user = SoftDeletingUser::create(['email' => 'test@example.com']);\n    $user->assignRole('testRole');\n    $user->delete();\n\n    $user = SoftDeletingUser::withTrashed()->find($user->id);\n\n    expect($user->hasRole('testRole'))->toBeTrue();\n});\n\nit('fires an event when a role is added', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->assignRole(['testRole', 'testRole2']);\n\n    $roleIds = app(Role::class)::whereIn('name', ['testRole', 'testRole2'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleAttachedEvent::class, function ($event) use ($roleIds) {\n        return $event->model instanceof User\n            && $event->model->hasRole('testRole')\n            && $event->model->hasRole('testRole2')\n            && $event->rolesOrIds === $roleIds;\n    });\n});\n\nit('fires an event when a role is removed', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    $this->testUser->removeRole('testRole', 'testRole2');\n\n    $roleIds = app(Role::class)::whereIn('name', ['testRole', 'testRole2'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleDetachedEvent::class, function ($event) use ($roleIds) {\n        return $event->model instanceof User\n            && ! $event->model->hasRole('testRole')\n            && ! $event->model->hasRole('testRole2')\n            && $event->rolesOrIds === $roleIds;\n    });\n});\n\nit('can be given a role on permission when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    $testPermission = app(Permission::class)->with('roles')->get()->first();\n\n    $testPermission->assignRole('testRole');\n\n    expect($testPermission->hasRole('testRole'))->toBeTrue();\n});\n\nit('can be given a role on user when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    User::create(['email' => 'other@user.com']);\n    $user = User::with('roles')->get()->first();\n    $user->assignRole('testRole');\n\n    expect($user->hasRole('testRole'))->toBeTrue();\n});\n\nit('fires detach event when syncing roles', function () {\n    Event::fake([RoleDetachedEvent::class, RoleAttachedEvent::class]);\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    app(Role::class)->create(['name' => 'testRole3']);\n\n    $this->testUser->syncRoles('testRole3');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n    expect($this->testUser->hasRole('testRole3'))->toBeTrue();\n\n    $removedRoleIds = app(Role::class)::whereIn('name', ['testRole', 'testRole2'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleDetachedEvent::class, function ($event) use ($removedRoleIds) {\n        return $event->model instanceof User\n            && ! $event->model->hasRole('testRole')\n            && ! $event->model->hasRole('testRole2')\n            && $event->rolesOrIds === $removedRoleIds;\n    });\n\n    $attachedRoleIds = app(Role::class)::whereIn('name', ['testRole3'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleAttachedEvent::class, function ($event) use ($attachedRoleIds) {\n        return $event->model instanceof User\n            && $event->model->hasRole('testRole3')\n            && $event->rolesOrIds === $attachedRoleIds;\n    });\n});\n"
  },
  {
    "path": "tests/Traits/HasRolesWithCustomModelsTest.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Support\\Carbon;\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminate\\Support\\Facades\\Event;\nuse Spatie\\Permission\\Contracts\\Permission as PermissionContract;\nuse Spatie\\Permission\\Contracts\\Role as RoleContract;\nuse Spatie\\Permission\\Events\\RoleAttachedEvent;\nuse Spatie\\Permission\\Events\\RoleDetachedEvent;\nuse Spatie\\Permission\\Exceptions\\GuardDoesNotMatch;\nuse Spatie\\Permission\\Exceptions\\RoleDoesNotExist;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Admin;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Role;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\SoftDeletingUser;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nbeforeEach(function () {\n    $this->setUpCustomModels();\n    $this->resetDatabaseQuery = config('cache.default') === 'database' ? 1 : 0;\n});\n\n// =======================================================================\n// Tests inherited from HasRolesTest (running with custom models)\n// =======================================================================\n\nit('can determine that the user does not have a role', function () {\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    $role = app(RoleContract::class)->findOrCreate('testRoleInWebGuard', 'web');\n\n    expect($this->testUser->hasRole($role))->toBeFalse();\n\n    $this->testUser->assignRole($role);\n    expect($this->testUser->hasRole($role))->toBeTrue();\n    expect($this->testUser->hasRole($role->name))->toBeTrue();\n    expect($this->testUser->hasRole($role->name, $role->guard_name))->toBeTrue();\n    expect($this->testUser->hasRole([$role->name, 'fakeRole'], $role->guard_name))->toBeTrue();\n    expect($this->testUser->hasRole($role->getKey(), $role->guard_name))->toBeTrue();\n    expect($this->testUser->hasRole([$role->getKey(), 'fakeRole'], $role->guard_name))->toBeTrue();\n\n    expect($this->testUser->hasRole($role->name, 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasRole([$role->name, 'fakeRole'], 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasRole($role->getKey(), 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasRole([$role->getKey(), 'fakeRole'], 'fakeGuard'))->toBeFalse();\n\n    $role = app(RoleContract::class)->findOrCreate('testRoleInWebGuard2', 'web');\n    expect($this->testUser->hasRole($role))->toBeFalse();\n});\n\nit('can assign and remove a role using enums', function () {\n    $enum1 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::UserManager;\n    $enum2 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::Writer;\n    $enum3 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::CastedEnum1;\n    $enum4 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::CastedEnum2;\n\n    app(RoleContract::class)->findOrCreate($enum1->value, 'web');\n    app(RoleContract::class)->findOrCreate($enum2->value, 'web');\n    app(RoleContract::class)->findOrCreate($enum3->value, 'web');\n    app(RoleContract::class)->findOrCreate($enum4->value, 'web');\n\n    expect($this->testUser->hasRole($enum1))->toBeFalse();\n    expect($this->testUser->hasRole($enum2))->toBeFalse();\n    expect($this->testUser->hasRole($enum3))->toBeFalse();\n    expect($this->testUser->hasRole($enum4))->toBeFalse();\n    expect($this->testUser->hasRole('user-manager'))->toBeFalse();\n    expect($this->testUser->hasRole('writer'))->toBeFalse();\n    expect($this->testUser->hasRole('casted_enum-1'))->toBeFalse();\n    expect($this->testUser->hasRole('casted_enum-2'))->toBeFalse();\n\n    $this->testUser->assignRole($enum1);\n    $this->testUser->assignRole($enum2);\n    $this->testUser->assignRole($enum3);\n    $this->testUser->assignRole($enum4);\n\n    expect($this->testUser->hasRole($enum1))->toBeTrue();\n    expect($this->testUser->hasRole($enum2))->toBeTrue();\n    expect($this->testUser->hasRole($enum3))->toBeTrue();\n    expect($this->testUser->hasRole($enum4))->toBeTrue();\n\n    expect($this->testUser->hasRole([$enum1, 'writer']))->toBeTrue();\n    expect($this->testUser->hasRole([$enum3, 'casted_enum-2']))->toBeTrue();\n\n    expect($this->testUser->hasAllRoles([$enum1, $enum2, $enum3, $enum4]))->toBeTrue();\n    expect($this->testUser->hasAllRoles(['user-manager', 'writer', 'casted_enum-1', 'casted_enum-2']))->toBeTrue();\n    expect($this->testUser->hasAllRoles([$enum1, $enum2, $enum3, $enum4, 'not exist']))->toBeFalse();\n    expect($this->testUser->hasAllRoles(['user-manager', 'writer', 'casted_enum-1', 'casted_enum-2', 'not exist']))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles([$enum4, $enum3, $enum2, $enum1]))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['user-manager', 'writer', 'casted_enum-1', 'casted_enum-2']))->toBeTrue();\n\n    $this->testUser->removeRole($enum1);\n\n    expect($this->testUser->hasRole($enum1))->toBeFalse();\n});\n\nit('can scope a role using enums', function () {\n    $enum1 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::UserManager;\n    $enum2 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::Writer;\n    $role1 = app(RoleContract::class)->findOrCreate($enum1->value, 'web');\n    $role2 = app(RoleContract::class)->findOrCreate($enum2->value, 'web');\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n\n    // assign only one user to a role\n    $user2->assignRole($enum1);\n    expect($user2->hasRole($enum1))->toBeTrue();\n    expect($user2->hasRole($enum2))->toBeFalse();\n\n    $scopedUsers1 = User::role($enum1)->get();\n    $scopedUsers2 = User::role($enum2)->get();\n    $scopedUsers3 = User::withoutRole($enum2)->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(0);\n    expect($scopedUsers3->count())->toEqual(3);\n});\n\nit('can assign and remove a role', function () {\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    $this->testUser->removeRole('testRole');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n});\n\nit('removes a role and returns roles', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->assignRole('testRole2');\n\n    expect($this->testUser->hasRole(['testRole', 'testRole2']))->toBeTrue();\n\n    $roles = $this->testUser->removeRole('testRole');\n\n    expect($roles->hasRole('testRole'))->toBeFalse();\n\n    expect($roles->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can assign and remove a role on a permission', function () {\n    $this->testUserPermission->assignRole('testRole');\n\n    expect($this->testUserPermission->hasRole('testRole'))->toBeTrue();\n\n    $this->testUserPermission->removeRole('testRole');\n\n    expect($this->testUserPermission->hasRole('testRole'))->toBeFalse();\n});\n\nit('can assign and remove a role using an object', function () {\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeTrue();\n\n    $this->testUser->removeRole($this->testUserRole);\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeFalse();\n});\n\nit('can assign and remove a role using an id', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeTrue();\n\n    $this->testUser->removeRole($this->testUserRole->getKey());\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeFalse();\n});\n\nit('can assign and remove multiple roles at once', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey(), 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n\n    $this->testUser->removeRole($this->testUserRole->getKey(), 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n});\n\nit('can assign and remove multiple roles using an array', function () {\n    $this->testUser->assignRole([$this->testUserRole->getKey(), 'testRole2']);\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n\n    $this->testUser->removeRole([$this->testUserRole->getKey(), 'testRole2']);\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n});\n\nit('does not remove already associated roles when assigning new roles', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    $this->testUser->assignRole('testRole2');\n\n    expect($this->testUser->fresh()->hasRole('testRole'))->toBeTrue();\n});\n\nit('does not throw an exception when assigning a role that is already assigned', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    expect($this->testUser->fresh()->hasRole('testRole'))->toBeTrue();\n});\n\nit('throws an exception when assigning a role that does not exist', function () {\n    expect(fn () => $this->testUser->assignRole('evil-emperor'))->toThrow(RoleDoesNotExist::class);\n});\n\nit('can only assign roles from the correct guard', function () {\n    expect(fn () => $this->testUser->assignRole('testAdminRole'))->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when assigning a role from a different guard', function () {\n    expect(fn () => $this->testUser->assignRole($this->testAdminRole))->toThrow(GuardDoesNotMatch::class);\n});\n\nit('ignores null roles when syncing', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->syncRoles('testRole2', null);\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can sync roles from a string', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->syncRoles('testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can sync roles from a string on a permission', function () {\n    $this->testUserPermission->assignRole('testRole');\n\n    $this->testUserPermission->syncRoles('testRole2');\n\n    expect($this->testUserPermission->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUserPermission->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can avoid sync duplicated roles', function () {\n    $this->testUser->syncRoles('testRole', 'testRole', 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can avoid detach on role that does not exist sync', function () {\n    $this->testUser->syncRoles('testRole');\n\n    expect(fn () => $this->testUser->syncRoles('role-does-not-exist'))->toThrow(RoleDoesNotExist::class);\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n    expect($this->testUser->hasRole('role-does-not-exist'))->toBeFalse();\n});\n\nit('can sync multiple roles', function () {\n    $this->testUser->syncRoles('testRole', 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can sync multiple roles from an array', function () {\n    $this->testUser->syncRoles(['testRole', 'testRole2']);\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('will remove all roles when an empty array is passed to sync roles', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->assignRole('testRole2');\n\n    $this->testUser->syncRoles([]);\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n});\n\nit('sync roles error does not detach roles', function () {\n    $this->testUser->assignRole('testRole');\n\n    expect(fn () => $this->testUser->syncRoles('testRole2', 'role-that-does-not-exist'))->toThrow(RoleDoesNotExist::class);\n\n    expect($this->testUser->fresh()->hasRole('testRole'))->toBeTrue();\n});\n\nit('will sync roles to a model that is not persisted', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncRoles([$this->testUserRole]);\n    $user->save();\n    $user->save(); // test save same model twice\n\n    expect($user->hasRole($this->testUserRole))->toBeTrue();\n\n    $user->syncRoles([$this->testUserRole]);\n    expect($user->hasRole($this->testUserRole))->toBeTrue();\n    expect($user->fresh()->hasRole($this->testUserRole))->toBeTrue();\n});\n\nit('does not run unnecessary sqls when assigning new roles', function () {\n    $role2 = app(RoleContract::class)->where('name', ['testRole2'])->first();\n\n    DB::enableQueryLog();\n    $this->testUser->syncRoles($this->testUserRole, $role2);\n    DB::disableQueryLog();\n\n    $necessaryQueriesCount = 2;\n\n    // Teams reloads relation, adding an extra query\n    if (app(PermissionRegistrar::class)->teams) {\n        $necessaryQueriesCount++;\n    }\n\n    expect(DB::getQueryLog())->toHaveCount($necessaryQueriesCount);\n});\n\nit('calling syncRoles before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncRoles('testRole');\n    $user->save();\n\n    $user2 = new User(['email' => 'admin@user.com']);\n    $user2->syncRoles('testRole2');\n\n    DB::enableQueryLog();\n    $user2->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasRole('testRole'))->toBeTrue();\n    expect($user->fresh()->hasRole('testRole2'))->toBeFalse();\n\n    expect($user2->fresh()->hasRole('testRole2'))->toBeTrue();\n    expect($user2->fresh()->hasRole('testRole'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('calling assignRole before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->assignRole('testRole');\n    $user->save();\n\n    $admin_user = new User(['email' => 'admin@user.com']);\n    $admin_user->assignRole('testRole2');\n\n    DB::enableQueryLog();\n    $admin_user->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasRole('testRole'))->toBeTrue();\n    expect($user->fresh()->hasRole('testRole2'))->toBeFalse();\n\n    expect($admin_user->fresh()->hasRole('testRole2'))->toBeTrue();\n    expect($admin_user->fresh()->hasRole('testRole'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('throws an exception when syncing a role from another guard', function () {\n    expect(fn () => $this->testUser->syncRoles('testRole', 'testAdminRole'))->toThrow(RoleDoesNotExist::class);\n\n    expect(fn () => $this->testUser->syncRoles('testRole', $this->testAdminRole))->toThrow(GuardDoesNotMatch::class);\n});\n\nit('deletes pivot table entries when deleting models', function () {\n    $user = User::create(['email' => 'user@test.com']);\n\n    $user->assignRole('testRole');\n    $user->givePermissionTo('edit-articles');\n\n    $this->assertDatabaseHas('model_has_permissions', [config('permission.column_names.model_morph_key') => $user->id]);\n    $this->assertDatabaseHas('model_has_roles', [config('permission.column_names.model_morph_key') => $user->id]);\n\n    $user->delete();\n\n    $this->assertDatabaseMissing('model_has_permissions', [config('permission.column_names.model_morph_key') => $user->id]);\n    $this->assertDatabaseMissing('model_has_roles', [config('permission.column_names.model_morph_key') => $user->id]);\n});\n\nit('can scope users using a string', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n\n    $scopedUsers = User::role('testRole')->get();\n\n    expect($scopedUsers->count())->toEqual(1);\n});\n\nit('can withoutscope users using a string', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers = User::withoutRole('testRole2')->get();\n\n    expect($scopedUsers->count())->toEqual(1);\n});\n\nit('can scope users using an array', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role([$this->testUserRole])->get();\n    $scopedUsers2 = User::role(['testRole', 'testRole2'])->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(2);\n});\n\nit('can withoutscope users using an array', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole([$this->testUserRole])->get();\n    $scopedUsers2 = User::withoutRole([$this->testUserRole->name, 'testRole2'])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(0);\n});\n\nit('can scope users using an array of ids and names', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $firstAssignedRoleName = $this->testUserRole->name;\n    $secondAssignedRoleId = app(RoleContract::class)->findByName('testRole2')->getKey();\n\n    $scopedUsers = User::role([$firstAssignedRoleName, $secondAssignedRoleId])->get();\n\n    expect($scopedUsers->count())->toEqual(2);\n});\n\nit('can withoutscope users using an array of ids and names', function () {\n    app(RoleContract::class)->create(['name' => 'testRole3']);\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $firstAssignedRoleName = $this->testUserRole->name;\n    $unassignedRoleId = app(RoleContract::class)->findByName('testRole3')->getKey();\n\n    $scopedUsers = User::withoutRole([$firstAssignedRoleName, $unassignedRoleId])->get();\n\n    expect($scopedUsers->count())->toEqual(2);\n});\n\nit('can scope users using a collection', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role([$this->testUserRole])->get();\n    $scopedUsers2 = User::role(collect(['testRole', 'testRole2']))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(2);\n});\n\nit('can withoutscope users using a collection', function () {\n    app(RoleContract::class)->create(['name' => 'testRole3']);\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole([$this->testUserRole])->get();\n    $scopedUsers2 = User::withoutRole(collect(['testRole', 'testRole3']))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('can scope users using an object', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role($this->testUserRole)->get();\n    $scopedUsers2 = User::role([$this->testUserRole])->get();\n    $scopedUsers3 = User::role(collect([$this->testUserRole]))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(1);\n});\n\nit('can withoutscope users using an object', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole($this->testUserRole)->get();\n    $scopedUsers2 = User::withoutRole([$this->testUserRole])->get();\n    $scopedUsers3 = User::withoutRole(collect([$this->testUserRole]))->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(2);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope against a specific guard', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role('testRole', 'web')->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n\n    $user3 = Admin::create(['email' => 'user3@test.com']);\n    $user4 = Admin::create(['email' => 'user4@test.com']);\n    $user5 = Admin::create(['email' => 'user5@test.com']);\n    $testAdminRole2 = app(RoleContract::class)->create(['name' => 'testAdminRole2', 'guard_name' => 'admin']);\n    $user3->assignRole($this->testAdminRole);\n    $user4->assignRole($this->testAdminRole);\n    $user5->assignRole($testAdminRole2);\n    $scopedUsers2 = Admin::role('testAdminRole', 'admin')->get();\n    $scopedUsers3 = Admin::role('testAdminRole2', 'admin')->get();\n\n    expect($scopedUsers2->count())->toEqual(2);\n    expect($scopedUsers3->count())->toEqual(1);\n});\n\nit('can withoutscope against a specific guard', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole('testRole', 'web')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n\n    Admin::all()->each(fn ($item) => $item->delete());\n    $user4 = Admin::create(['email' => 'user4@test.com']);\n    $user5 = Admin::create(['email' => 'user5@test.com']);\n    $user6 = Admin::create(['email' => 'user6@test.com']);\n    $testAdminRole2 = app(RoleContract::class)->create(['name' => 'testAdminRole2', 'guard_name' => 'admin']);\n    $user4->assignRole($this->testAdminRole);\n    $user5->assignRole($this->testAdminRole);\n    $user6->assignRole($testAdminRole2);\n    $scopedUsers2 = Admin::withoutRole('testAdminRole', 'admin')->get();\n    $scopedUsers3 = Admin::withoutRole('testAdminRole2', 'admin')->get();\n\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('throws an exception when trying to scope a role from another guard', function () {\n    expect(fn () => User::role('testAdminRole')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when trying to call withoutscope on a role from another guard', function () {\n    expect(fn () => User::withoutRole('testAdminRole')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when trying to scope a non existing role', function () {\n    expect(fn () => User::role('role not defined')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when trying to use withoutscope on a non existing role', function () {\n    expect(fn () => User::withoutRole('role not defined')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('can determine that a user has one of the given roles', function () {\n    $roleModel = app(RoleContract::class);\n\n    $roleModel->create(['name' => 'second role']);\n\n    expect($this->testUser->hasRole($roleModel->all()))->toBeFalse();\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasRole($roleModel->all()))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole($roleModel->all()))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole('role does not exist'))->toBeFalse();\n\n    expect($this->testUser->hasAnyRole(['testRole']))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole(['testRole', 'role does not exist']))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole(['role does not exist']))->toBeFalse();\n\n    expect($this->testUser->hasAnyRole('testRole', 'role does not exist'))->toBeTrue();\n});\n\nit('can determine that a user has all of the given roles', function () {\n    $roleModel = app(RoleContract::class);\n\n    expect($this->testUser->hasAllRoles($roleModel->first()))->toBeFalse();\n\n    expect($this->testUser->hasAllRoles('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasAllRoles($roleModel->all()))->toBeFalse();\n\n    $roleModel->create(['name' => 'second role']);\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasAllRoles('testRole'))->toBeTrue();\n    expect($this->testUser->hasAllRoles('testRole', 'web'))->toBeTrue();\n    expect($this->testUser->hasAllRoles('testRole', 'fakeGuard'))->toBeFalse();\n\n    expect($this->testUser->hasAllRoles(['testRole', 'second role']))->toBeFalse();\n    expect($this->testUser->hasAllRoles(['testRole', 'second role'], 'web'))->toBeFalse();\n\n    $this->testUser->assignRole('second role');\n\n    expect($this->testUser->hasAllRoles(['testRole', 'second role']))->toBeTrue();\n    expect($this->testUser->hasAllRoles(['testRole', 'second role'], 'web'))->toBeTrue();\n    expect($this->testUser->hasAllRoles(['testRole', 'second role'], 'fakeGuard'))->toBeFalse();\n});\n\nit('can determine that a user has exact all of the given roles', function () {\n    $roleModel = app(RoleContract::class);\n\n    expect($this->testUser->hasExactRoles($roleModel->first()))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles($roleModel->all()))->toBeFalse();\n\n    $roleModel->create(['name' => 'second role']);\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasExactRoles('testRole'))->toBeTrue();\n    expect($this->testUser->hasExactRoles('testRole', 'web'))->toBeTrue();\n    expect($this->testUser->hasExactRoles('testRole', 'fakeGuard'))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles(['testRole', 'second role']))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'web'))->toBeFalse();\n\n    $this->testUser->assignRole('second role');\n\n    expect($this->testUser->hasExactRoles(['testRole', 'second role']))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'web'))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'fakeGuard'))->toBeFalse();\n\n    $roleModel->create(['name' => 'third role']);\n    $this->testUser->assignRole('third role');\n\n    expect($this->testUser->hasExactRoles(['testRole', 'second role']))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'web'))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role', 'third role']))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role', 'third role'], 'web'))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role', 'third role'], 'fakeGuard'))->toBeFalse();\n});\n\nit('can determine that a user does not have a role from another guard', function () {\n    expect($this->testUser->hasRole('testAdminRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole($this->testAdminRole))->toBeFalse();\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAnyRole(['testRole', 'testAdminRole']))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole('testAdminRole', $this->testAdminRole))->toBeFalse();\n});\n\nit('can check against any multiple roles using multiple arguments', function () {\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAnyRole($this->testAdminRole, ['testRole'], 'This Role Does Not Even Exist'))->toBeTrue();\n});\n\nit('returns false instead of an exception when checking against any undefined roles using multiple arguments', function () {\n    expect($this->testUser->hasAnyRole('This Role Does Not Even Exist', $this->testAdminRole))->toBeFalse();\n});\n\nit('throws an exception if an unsupported type is passed to hasRoles', function () {\n    expect(fn () => $this->testUser->hasRole(new class {}))->toThrow(\\TypeError::class);\n});\n\nit('can retrieve role names', function () {\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    expect($this->testUser->getRoleNames()->sort()->values())->toEqual(\n        collect(['testRole', 'testRole2'])\n    );\n});\n\nit('does not detach roles when user soft deleting', function () {\n    $user = SoftDeletingUser::create(['email' => 'test@example.com']);\n    $user->assignRole('testRole');\n    $user->delete();\n\n    $user = SoftDeletingUser::withTrashed()->find($user->id);\n\n    expect($user->hasRole('testRole'))->toBeTrue();\n});\n\nit('fires an event when a role is added', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->assignRole(['testRole', 'testRole2']);\n\n    $roleIds = app(RoleContract::class)::whereIn('name', ['testRole', 'testRole2'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleAttachedEvent::class, function ($event) use ($roleIds) {\n        return $event->model instanceof User\n            && $event->model->hasRole('testRole')\n            && $event->model->hasRole('testRole2')\n            && $event->rolesOrIds === $roleIds;\n    });\n});\n\nit('fires an event when a role is removed', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    $this->testUser->removeRole('testRole', 'testRole2');\n\n    $roleIds = app(RoleContract::class)::whereIn('name', ['testRole', 'testRole2'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleDetachedEvent::class, function ($event) use ($roleIds) {\n        return $event->model instanceof User\n            && ! $event->model->hasRole('testRole')\n            && ! $event->model->hasRole('testRole2')\n            && $event->rolesOrIds === $roleIds;\n    });\n});\n\nit('can be given a role on permission when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    $testPermission = app(PermissionContract::class)->with('roles')->get()->first();\n\n    $testPermission->assignRole('testRole');\n\n    expect($testPermission->hasRole('testRole'))->toBeTrue();\n});\n\nit('can be given a role on user when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    User::create(['email' => 'other@user.com']);\n    $user = User::with('roles')->get()->first();\n    $user->assignRole('testRole');\n\n    expect($user->hasRole('testRole'))->toBeTrue();\n});\n\nit('fires detach event when syncing roles', function () {\n    Event::fake([RoleDetachedEvent::class, RoleAttachedEvent::class]);\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    app(RoleContract::class)->create(['name' => 'testRole3']);\n\n    $this->testUser->syncRoles('testRole3');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n    expect($this->testUser->hasRole('testRole3'))->toBeTrue();\n\n    $removedRoleIds = app(RoleContract::class)::whereIn('name', ['testRole', 'testRole2'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleDetachedEvent::class, function ($event) use ($removedRoleIds) {\n        return $event->model instanceof User\n            && ! $event->model->hasRole('testRole')\n            && ! $event->model->hasRole('testRole2')\n            && $event->rolesOrIds === $removedRoleIds;\n    });\n\n    $attachedRoleIds = app(RoleContract::class)::whereIn('name', ['testRole3'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleAttachedEvent::class, function ($event) use ($attachedRoleIds) {\n        return $event->model instanceof User\n            && $event->model->hasRole('testRole3')\n            && $event->rolesOrIds === $attachedRoleIds;\n    });\n});\n\n// =======================================================================\n// Custom model-specific tests\n// =======================================================================\n\nit('can use custom model role', function () {\n    expect(get_class($this->testUserRole))->toBe(Role::class);\n});\n\nit('doesnt detach permissions when soft deleting', function () {\n    $this->testUserRole->givePermissionTo($this->testUserPermission);\n\n    DB::enableQueryLog();\n    $this->testUserRole->delete();\n    DB::disableQueryLog();\n\n    expect(count(DB::getQueryLog()))->toBe(1 + $this->resetDatabaseQuery);\n\n    $role = Role::onlyTrashed()->find($this->testUserRole->getKey());\n\n    expect(DB::table(config('permission.table_names.role_has_permissions'))->where('role_test_id', $role->getKey())->count())->toEqual(1);\n});\n\nit('doesnt detach users when soft deleting', function () {\n    $this->testUser->assignRole($this->testUserRole);\n\n    DB::enableQueryLog();\n    $this->testUserRole->delete();\n    DB::disableQueryLog();\n\n    expect(count(DB::getQueryLog()))->toBe(1 + $this->resetDatabaseQuery);\n\n    $role = Role::onlyTrashed()->find($this->testUserRole->getKey());\n\n    expect(DB::table(config('permission.table_names.model_has_roles'))->where('role_test_id', $role->getKey())->count())->toEqual(1);\n});\n\nit('does detach permissions and users when force deleting', function () {\n    $role_id = $this->testUserRole->getKey();\n    $this->testUserPermission->assignRole($role_id);\n    $this->testUser->assignRole($role_id);\n\n    DB::enableQueryLog();\n    $this->testUserRole->forceDelete();\n    DB::disableQueryLog();\n\n    expect(count(DB::getQueryLog()))->toBe(3 + $this->resetDatabaseQuery);\n\n    $role = Role::withTrashed()->find($role_id);\n\n    expect($role)->toBeNull();\n    expect(DB::table(config('permission.table_names.role_has_permissions'))->where('role_test_id', $role_id)->count())->toEqual(0);\n    expect(DB::table(config('permission.table_names.model_has_roles'))->where('role_test_id', $role_id)->count())->toEqual(0);\n});\n\nit('should touch when assigning new roles', function () {\n    Carbon::setTestNow('2021-07-19 10:13:14');\n\n    $user = Admin::create(['email' => 'user1@test.com']);\n    $role1 = app(Role::class)->create(['name' => 'testRoleInWebGuard', 'guard_name' => 'admin']);\n    $role2 = app(Role::class)->create(['name' => 'testRoleInWebGuard1', 'guard_name' => 'admin']);\n\n    expect($role1->updated_at->format('Y-m-d H:i:s'))->toBe('2021-07-19 10:13:14');\n\n    Carbon::setTestNow('2021-07-20 19:13:14');\n\n    $user->syncRoles([$role1->getKey(), $role2->getKey()]);\n\n    expect($role1->refresh()->updated_at->format('Y-m-d H:i:s'))->toBe('2021-07-20 19:13:14');\n    expect($role2->refresh()->updated_at->format('Y-m-d H:i:s'))->toBe('2021-07-20 19:13:14');\n});\n"
  },
  {
    "path": "tests/Traits/TeamHasPermissionsTest.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Support\\Facades\\Event;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Events\\PermissionAttachedEvent;\nuse Spatie\\Permission\\Events\\PermissionDetachedEvent;\nuse Spatie\\Permission\\Exceptions\\GuardDoesNotMatch;\nuse Spatie\\Permission\\Exceptions\\PermissionDoesNotExist;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\SoftDeletingUser;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nbeforeEach(fn () => $this->setUpTeams());\n\n// ---- Tests inherited from HasPermissionsTest (running with $hasTeams = true) ----\n\nit('can assign a permission to a user', function () {\n    $this->testUser->givePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeTrue();\n});\n\nit('can assign a permission to a user with a non default guard', function () {\n    $testUserPermission = app(Permission::class)->create([\n        'name' => 'edit-articles',\n        'guard_name' => 'api',\n    ]);\n\n    $this->testUser->givePermissionTo($testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($testUserPermission))->toBeTrue();\n});\n\nit('throws an exception when assigning a permission that does not exist', function () {\n    expect(fn () => $this->testUser->givePermissionTo('permission-does-not-exist'))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when assigning a permission to a user from a different guard', function () {\n    expect(fn () => $this->testUser->givePermissionTo($this->testAdminPermission))->toThrow(GuardDoesNotMatch::class);\n\n    expect(fn () => $this->testUser->givePermissionTo('admin-permission'))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('can revoke a permission from a user', function () {\n    $this->testUser->givePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeTrue();\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeFalse();\n});\n\nit('can assign and remove a permission using enums', function () {\n    $enum = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::ViewArticles;\n\n    $permission = app(Permission::class)->findOrCreate($enum->value, 'web');\n\n    $this->testUser->givePermissionTo($enum);\n\n    expect($this->testUser->hasPermissionTo($enum))->toBeTrue();\n    expect($this->testUser->hasAnyPermission($enum))->toBeTrue();\n    expect($this->testUser->hasDirectPermission($enum))->toBeTrue();\n\n    $this->testUser->revokePermissionTo($enum);\n\n    expect($this->testUser->hasPermissionTo($enum))->toBeFalse();\n    expect($this->testUser->hasAnyPermission($enum))->toBeFalse();\n    expect($this->testUser->hasDirectPermission($enum))->toBeFalse();\n});\n\nit('can scope users using enums', function () {\n    $enum1 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::ViewArticles;\n    $enum2 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::EditArticles;\n    $permission1 = app(Permission::class)->findOrCreate($enum1->value, 'web');\n    $permission2 = app(Permission::class)->findOrCreate($enum2->value, 'web');\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo([$enum1, $enum2]);\n    $this->testUserRole->givePermissionTo($enum2);\n    $user2->assignRole('testRole');\n\n    $scopedUsers1 = User::permission($enum2)->get();\n    $scopedUsers2 = User::permission([$enum1])->get();\n    $scopedUsers3 = User::withoutPermission([$enum1])->get();\n    $scopedUsers4 = User::withoutPermission([$enum2])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n    expect($scopedUsers4->count())->toEqual(1);\n});\n\nit('can scope users using a string', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n\n    $scopedUsers1 = User::permission('edit-articles')->get();\n    $scopedUsers2 = User::permission(['edit-news'])->get();\n    $scopedUsers3 = User::withoutPermission('edit-news')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using a int', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo([1, 2]);\n    $this->testUserRole->givePermissionTo(1);\n    $user2->assignRole('testRole');\n\n    $scopedUsers1 = User::permission(1)->get();\n    $scopedUsers2 = User::permission([2])->get();\n    $scopedUsers3 = User::withoutPermission([2])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using an array', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::permission(['edit-articles', 'edit-news'])->get();\n    $scopedUsers2 = User::permission(['edit-news'])->get();\n    $scopedUsers3 = User::withoutPermission(['edit-news'])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using a collection', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::permission(collect(['edit-articles', 'edit-news']))->get();\n    $scopedUsers2 = User::permission(collect(['edit-news']))->get();\n    $scopedUsers3 = User::withoutPermission(collect(['edit-news']))->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope users using an object', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user1->givePermissionTo($this->testUserPermission->name);\n\n    $scopedUsers1 = User::permission($this->testUserPermission)->get();\n    $scopedUsers2 = User::permission([$this->testUserPermission])->get();\n    $scopedUsers3 = User::permission(collect([$this->testUserPermission]))->get();\n    $scopedUsers4 = User::withoutPermission(collect([$this->testUserPermission]))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(1);\n    expect($scopedUsers4->count())->toEqual(0);\n});\n\nit('can scope users without direct permissions only role', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::permission('edit-articles')->get();\n    $scopedUsers2 = User::withoutPermission('edit-articles')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('can scope users with only direct permission', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->givePermissionTo(['edit-news']);\n    $user2->givePermissionTo(['edit-articles', 'edit-news']);\n\n    $scopedUsers1 = User::permission('edit-news')->get();\n    $scopedUsers2 = User::withoutPermission('edit-news')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('throws an exception when calling hasPermissionTo with an invalid type', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasPermissionTo(new \\stdClass))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when calling hasPermissionTo with null', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasPermissionTo(null))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when calling hasDirectPermission with an invalid type', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasDirectPermission(new \\stdClass))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when calling hasDirectPermission with null', function () {\n    $user = User::create(['email' => 'user1@test.com']);\n\n    expect(fn () => $user->hasDirectPermission(null))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when trying to scope a non existing permission', function () {\n    expect(fn () => User::permission('not defined permission')->get())->toThrow(PermissionDoesNotExist::class);\n\n    expect(fn () => User::withoutPermission('not defined permission')->get())->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when trying to scope a permission from another guard', function () {\n    expect(fn () => User::permission('testAdminPermission')->get())->toThrow(PermissionDoesNotExist::class);\n\n    expect(fn () => User::withoutPermission('testAdminPermission')->get())->toThrow(PermissionDoesNotExist::class);\n});\n\nit('doesnt detach permissions when user soft deleting', function () {\n    $user = SoftDeletingUser::create(['email' => 'test@example.com']);\n    $user->givePermissionTo(['edit-news']);\n    $user->delete();\n\n    $user = SoftDeletingUser::withTrashed()->find($user->id);\n\n    expect($user->hasPermissionTo('edit-news'))->toBeTrue();\n});\n\nit('can give and revoke multiple permissions', function () {\n    $this->testUserRole->givePermissionTo(['edit-articles', 'edit-news']);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(2);\n\n    $this->testUserRole->revokePermissionTo(['edit-articles', 'edit-news']);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(0);\n});\n\nit('can give and revoke permissions models array', function () {\n    $models = [app(Permission::class)::where('name', 'edit-articles')->first(), app(Permission::class)::where('name', 'edit-news')->first()];\n\n    $this->testUserRole->givePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(2);\n\n    $this->testUserRole->revokePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(0);\n});\n\nit('can give and revoke permissions models collection', function () {\n    $models = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-news'])->get();\n\n    $this->testUserRole->givePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(2);\n\n    $this->testUserRole->revokePermissionTo($models);\n\n    expect($this->testUserRole->permissions()->count())->toEqual(0);\n});\n\nit('can determine that the user does not have a permission', function () {\n    expect($this->testUser->hasPermissionTo('edit-articles'))->toBeFalse();\n});\n\nit('throws an exception when the permission does not exist', function () {\n    expect(fn () => $this->testUser->hasPermissionTo('does-not-exist'))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('throws an exception when the permission does not exist for this guard', function () {\n    expect(fn () => $this->testUser->hasPermissionTo('does-not-exist', 'web'))->toThrow(PermissionDoesNotExist::class);\n});\n\nit('can reject a user that does not have any permissions at all', function () {\n    $user = new User;\n\n    expect($user->hasPermissionTo('edit-articles'))->toBeFalse();\n});\n\nit('can determine that the user has any of the permissions directly', function () {\n    expect($this->testUser->hasAnyPermission('edit-articles'))->toBeFalse();\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAnyPermission('edit-news', 'edit-articles'))->toBeTrue();\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasAnyPermission('edit-articles', 'edit-news'))->toBeTrue();\n    expect($this->testUser->hasAnyPermission('edit-blog', 'Edit News', ['Edit News']))->toBeFalse();\n});\n\nit('can determine that the user has any of the permissions directly using an array', function () {\n    expect($this->testUser->hasAnyPermission(['edit-articles']))->toBeFalse();\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAnyPermission(['edit-news', 'edit-articles']))->toBeTrue();\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasAnyPermission(['edit-articles', 'edit-news']))->toBeTrue();\n});\n\nit('can determine that the user has any of the permissions via role', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAnyPermission('edit-news', 'edit-articles'))->toBeTrue();\n    expect($this->testUser->hasAnyPermission('edit-blog', 'Edit News', ['Edit News']))->toBeFalse();\n});\n\nit('can determine that the user has all of the permissions directly', function () {\n    $this->testUser->givePermissionTo('edit-articles', 'edit-news');\n\n    expect($this->testUser->hasAllPermissions('edit-articles', 'edit-news'))->toBeTrue();\n\n    $this->testUser->revokePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAllPermissions('edit-articles', 'edit-news'))->toBeFalse();\n    expect($this->testUser->hasAllPermissions(['edit-articles', 'edit-news'], 'edit-blog'))->toBeFalse();\n});\n\nit('can determine that the user has all of the permissions directly using an array', function () {\n    expect($this->testUser->hasAllPermissions(['edit-articles', 'edit-news']))->toBeFalse();\n\n    $this->testUser->revokePermissionTo('edit-articles');\n\n    expect($this->testUser->hasAllPermissions(['edit-news', 'edit-articles']))->toBeFalse();\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->revokePermissionTo($this->testUserPermission);\n\n    expect($this->testUser->hasAllPermissions(['edit-articles', 'edit-news']))->toBeFalse();\n});\n\nit('can determine that the user has all of the permissions via role', function () {\n    $this->testUserRole->givePermissionTo('edit-articles', 'edit-news');\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAllPermissions('edit-articles', 'edit-news'))->toBeTrue();\n});\n\nit('can determine that user has direct permission', function () {\n    $this->testUser->givePermissionTo('edit-articles');\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->getDirectPermissions()->pluck('name'))->toEqual(collect(['edit-articles']));\n\n    $this->testUser->revokePermissionTo('edit-articles');\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeFalse();\n\n    $this->testUser->assignRole('testRole');\n    $this->testUserRole->givePermissionTo('edit-articles');\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeFalse();\n});\n\nit('can list all the permissions via roles of user', function () {\n    $roleModel = app(Role::class);\n    $roleModel->findByName('testRole2')->givePermissionTo('edit-news');\n\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    expect($this->testUser->getPermissionsViaRoles()->pluck('name')->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n});\n\nit('can list all the coupled permissions both directly and via roles', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->getAllPermissions()->pluck('name')->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n});\n\nit('can sync multiple permissions', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->syncPermissions('edit-articles', 'edit-blog');\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-news'))->toBeFalse();\n});\n\nit('can avoid sync duplicated permissions', function () {\n    $this->testUser->syncPermissions('edit-articles', 'edit-blog', 'edit-blog');\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n});\n\nit('can avoid detach on permission that does not exist sync', function () {\n    $this->testUser->syncPermissions('edit-articles');\n\n    expect(fn () => $this->testUser->syncPermissions('permission-does-not-exist'))->toThrow(PermissionDoesNotExist::class);\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->checkPermissionTo('permission-does-not-exist'))->toBeFalse();\n});\n\nit('can sync multiple permissions by id', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $ids = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-blog'])->pluck($this->testUserPermission->getKeyName());\n\n    $this->testUser->syncPermissions($ids);\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-news'))->toBeFalse();\n});\n\nit('sync permission ignores null inputs', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $ids = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-blog'])->pluck($this->testUserPermission->getKeyName());\n\n    $ids->push(null);\n\n    $this->testUser->syncPermissions($ids);\n\n    expect($this->testUser->hasDirectPermission('edit-articles'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-blog'))->toBeTrue();\n    expect($this->testUser->hasDirectPermission('edit-news'))->toBeFalse();\n});\n\nit('sync permission error does not detach permissions', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    expect(fn () => $this->testUser->syncPermissions('edit-articles', 'permission-that-does-not-exist'))->toThrow(PermissionDoesNotExist::class);\n\n    expect($this->testUser->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('does not remove already associated permissions when assigning new permissions', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->givePermissionTo('edit-articles');\n\n    expect($this->testUser->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('does not throw an exception when assigning a permission that is already assigned', function () {\n    $this->testUser->givePermissionTo('edit-news');\n\n    $this->testUser->givePermissionTo('edit-news');\n\n    expect($this->testUser->fresh()->hasDirectPermission('edit-news'))->toBeTrue();\n});\n\nit('can sync permissions to a model that is not persisted', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncPermissions('edit-articles');\n    $user->save();\n    $user->save(); // test save same model twice\n\n    expect($user->hasPermissionTo('edit-articles'))->toBeTrue();\n\n    $user->syncPermissions('edit-articles');\n    expect($user->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($user->fresh()->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n\nit('does not run unnecessary sqls when assigning new permissions', function () {\n    $permission2 = app(Permission::class)->where('name', ['edit-news'])->first();\n\n    DB::enableQueryLog();\n    $this->testUser->syncPermissions($this->testUserPermission, $permission2);\n    DB::disableQueryLog();\n\n    $necessaryQueriesCount = 2;\n\n    expect(DB::getQueryLog())->toHaveCount($necessaryQueriesCount);\n});\n\nit('calling givePermissionTo before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->givePermissionTo('edit-news');\n    $user->save();\n\n    $user2 = new User(['email' => 'test2@user.com']);\n    $user2->givePermissionTo('edit-articles');\n\n    DB::enableQueryLog();\n    $user2->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasPermissionTo('edit-news'))->toBeTrue();\n    expect($user->fresh()->hasPermissionTo('edit-articles'))->toBeFalse();\n\n    expect($user2->fresh()->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($user2->fresh()->hasPermissionTo('edit-news'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('calling syncPermissions before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncPermissions('edit-news');\n    $user->save();\n\n    $user2 = new User(['email' => 'test2@user.com']);\n    $user2->syncPermissions('edit-articles');\n\n    DB::enableQueryLog();\n    $user2->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasPermissionTo('edit-news'))->toBeTrue();\n    expect($user->fresh()->hasPermissionTo('edit-articles'))->toBeFalse();\n\n    expect($user2->fresh()->hasPermissionTo('edit-articles'))->toBeTrue();\n    expect($user2->fresh()->hasPermissionTo('edit-news'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('can retrieve permission names', function () {\n    $this->testUser->givePermissionTo('edit-news', 'edit-articles');\n    expect($this->testUser->getPermissionNames()->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n});\n\nit('can check many direct permissions', function () {\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n    expect($this->testUser->hasAllDirectPermissions(['edit-news', 'edit-articles']))->toBeTrue();\n    expect($this->testUser->hasAllDirectPermissions('edit-news', 'edit-articles'))->toBeTrue();\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-news', 'edit-blog']))->toBeFalse();\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-news'], 'edit-blog'))->toBeFalse();\n});\n\nit('can check if there is any of the direct permissions given', function () {\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n    expect($this->testUser->hasAnyDirectPermission(['edit-news', 'edit-blog']))->toBeTrue();\n    expect($this->testUser->hasAnyDirectPermission('edit-news', 'edit-blog'))->toBeTrue();\n    expect($this->testUser->hasAnyDirectPermission('edit-blog', 'Edit News', ['Edit News']))->toBeFalse();\n});\n\nit('can check permission based on logged in user guard', function () {\n    $this->testUser->givePermissionTo(app(Permission::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'api',\n    ]));\n    $response = $this->actingAs($this->testUser, 'api')\n        ->json('GET', '/check-api-guard-permission');\n    $response->assertJson([\n        'status' => true,\n    ]);\n});\n\nit('can reject permission based on logged in user guard', function () {\n    $unassignedPermission = app(Permission::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'api',\n    ]);\n\n    $assignedPermission = app(Permission::class)::create([\n        'name' => 'do_that',\n        'guard_name' => 'web',\n    ]);\n\n    $this->testUser->givePermissionTo($assignedPermission);\n    $response = $this->withExceptionHandling()\n        ->actingAs($this->testUser, 'api')\n        ->json('GET', '/check-api-guard-permission');\n    $response->assertJson([\n        'status' => false,\n    ]);\n});\n\nit('fires an event when a permission is added', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n\n    $ids = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-news'])\n        ->pluck($this->testUserPermission->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(PermissionAttachedEvent::class, function ($event) use ($ids) {\n        return $event->model instanceof User\n            && $event->model->hasPermissionTo('edit-news')\n            && $event->model->hasPermissionTo('edit-articles')\n            && $ids === $event->permissionsOrIds;\n    });\n});\n\nit('does not fire an event when events are not enabled', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', false);\n\n    $this->testUser->givePermissionTo(['edit-articles', 'edit-news']);\n\n    $ids = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-news'])\n        ->pluck($this->testUserPermission->getKeyName())\n        ->toArray();\n\n    Event::assertNotDispatched(PermissionAttachedEvent::class);\n});\n\nit('fires an event when a permission is removed', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $permissions = app(Permission::class)::whereIn('name', ['edit-articles', 'edit-news'])->get();\n\n    $this->testUser->givePermissionTo($permissions);\n\n    $this->testUser->revokePermissionTo($permissions);\n\n    Event::assertDispatched(PermissionDetachedEvent::class, function ($event) use ($permissions) {\n        return $event->model instanceof User\n            && ! $event->model->hasPermissionTo('edit-news')\n            && ! $event->model->hasPermissionTo('edit-articles')\n            && $event->permissionsOrIds === $permissions;\n    });\n});\n\nit('can be given a permission on role when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    $testRole = app(Role::class)->with('permissions')->get()->first();\n\n    $testRole->givePermissionTo('edit-articles');\n\n    expect($testRole->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n\nit('can be given a permission on user when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    User::create(['email' => 'other@user.com']);\n    $testUser = User::with('permissions')->get()->first();\n\n    $testUser->givePermissionTo('edit-articles');\n\n    expect($testUser->hasPermissionTo('edit-articles'))->toBeTrue();\n});\n\n// ---- Team-specific tests ----\n\nit('can assign same and different permission on same user on different teams', function () {\n    setPermissionsTeamId(1);\n    $this->testUser->givePermissionTo('edit-articles', 'edit-news');\n\n    setPermissionsTeamId(2);\n    $this->testUser->givePermissionTo('edit-articles', 'edit-blog');\n\n    setPermissionsTeamId(1);\n    $this->testUser->load('permissions');\n    expect($this->testUser->getPermissionNames()->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-news']))->toBeTrue();\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-blog']))->toBeFalse();\n\n    setPermissionsTeamId(2);\n    $this->testUser->load('permissions');\n    expect($this->testUser->getPermissionNames()->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-blog']));\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-blog']))->toBeTrue();\n    expect($this->testUser->hasAllDirectPermissions(['edit-articles', 'edit-news']))->toBeFalse();\n});\n\nit('can list all the coupled permissions both directly and via roles on same user on different teams', function () {\n    $this->testUserRole->givePermissionTo('edit-articles');\n\n    setPermissionsTeamId(1);\n    $this->testUser->assignRole('testRole');\n    $this->testUser->givePermissionTo('edit-news');\n\n    setPermissionsTeamId(2);\n    $this->testUser->assignRole('testRole');\n    $this->testUser->givePermissionTo('edit-blog');\n\n    setPermissionsTeamId(1);\n    $this->testUser->load('roles', 'permissions');\n\n    expect($this->testUser->getAllPermissions()->pluck('name')->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n\n    setPermissionsTeamId(2);\n    $this->testUser->load('roles', 'permissions');\n\n    expect($this->testUser->getAllPermissions()->pluck('name')->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-blog']));\n});\n\nit('can sync or remove permission without detach on different teams', function () {\n    setPermissionsTeamId(1);\n    $this->testUser->syncPermissions('edit-articles', 'edit-news');\n\n    setPermissionsTeamId(2);\n    $this->testUser->syncPermissions('edit-articles', 'edit-blog');\n\n    setPermissionsTeamId(1);\n    $this->testUser->load('permissions');\n\n    expect($this->testUser->getPermissionNames()->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-news']));\n\n    $this->testUser->revokePermissionTo('edit-articles');\n    expect($this->testUser->getPermissionNames()->sort()->values())\n        ->toEqual(collect(['edit-news']));\n\n    setPermissionsTeamId(2);\n    $this->testUser->load('permissions');\n    expect($this->testUser->getPermissionNames()->sort()->values())\n        ->toEqual(collect(['edit-articles', 'edit-blog']));\n});\n\nit('can scope users on different teams', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n\n    setPermissionsTeamId(2);\n    $user1->givePermissionTo(['edit-articles', 'edit-news']);\n    $this->testUserRole->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n\n    setPermissionsTeamId(1);\n    $user1->givePermissionTo(['edit-articles']);\n\n    setPermissionsTeamId(2);\n    $scopedUsers1Team2 = User::permission(['edit-articles', 'edit-news'])->get();\n    $scopedUsers2Team2 = User::permission('edit-news')->get();\n\n    expect($scopedUsers1Team2->count())->toEqual(2);\n    expect($scopedUsers2Team2->count())->toEqual(1);\n\n    setPermissionsTeamId(1);\n    $scopedUsers1Team1 = User::permission(['edit-articles', 'edit-news'])->get();\n    $scopedUsers2Team1 = User::permission('edit-news')->get();\n\n    expect($scopedUsers1Team1->count())->toEqual(1);\n    expect($scopedUsers2Team1->count())->toEqual(0);\n});\n"
  },
  {
    "path": "tests/Traits/TeamHasRolesTest.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminate\\Support\\Facades\\Event;\nuse Spatie\\Permission\\Contracts\\Permission;\nuse Spatie\\Permission\\Contracts\\Role;\nuse Spatie\\Permission\\Events\\RoleAttachedEvent;\nuse Spatie\\Permission\\Events\\RoleDetachedEvent;\nuse Spatie\\Permission\\Exceptions\\GuardDoesNotMatch;\nuse Spatie\\Permission\\Exceptions\\RoleDoesNotExist;\nuse Spatie\\Permission\\PermissionRegistrar;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\Admin;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\SoftDeletingUser;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\n\nbeforeEach(fn () => $this->setUpTeams());\n\n// ---- Tests inherited from HasRolesTest (running under TeamTestCase with $hasTeams = true) ----\n\nit('can determine that the user does not have a role', function () {\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    $role = app(Role::class)->findOrCreate('testRoleInWebGuard', 'web');\n\n    expect($this->testUser->hasRole($role))->toBeFalse();\n\n    $this->testUser->assignRole($role);\n    expect($this->testUser->hasRole($role))->toBeTrue();\n    expect($this->testUser->hasRole($role->name))->toBeTrue();\n    expect($this->testUser->hasRole($role->name, $role->guard_name))->toBeTrue();\n    expect($this->testUser->hasRole([$role->name, 'fakeRole'], $role->guard_name))->toBeTrue();\n    expect($this->testUser->hasRole($role->getKey(), $role->guard_name))->toBeTrue();\n    expect($this->testUser->hasRole([$role->getKey(), 'fakeRole'], $role->guard_name))->toBeTrue();\n\n    expect($this->testUser->hasRole($role->name, 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasRole([$role->name, 'fakeRole'], 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasRole($role->getKey(), 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasRole([$role->getKey(), 'fakeRole'], 'fakeGuard'))->toBeFalse();\n\n    $role = app(Role::class)->findOrCreate('testRoleInWebGuard2', 'web');\n    expect($this->testUser->hasRole($role))->toBeFalse();\n});\n\nit('can assign and remove a role using enums', function () {\n    $enum1 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::UserManager;\n    $enum2 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::Writer;\n    $enum3 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::CastedEnum1;\n    $enum4 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::CastedEnum2;\n\n    app(Role::class)->findOrCreate($enum1->value, 'web');\n    app(Role::class)->findOrCreate($enum2->value, 'web');\n    app(Role::class)->findOrCreate($enum3->value, 'web');\n    app(Role::class)->findOrCreate($enum4->value, 'web');\n\n    expect($this->testUser->hasRole($enum1))->toBeFalse();\n    expect($this->testUser->hasRole($enum2))->toBeFalse();\n    expect($this->testUser->hasRole($enum3))->toBeFalse();\n    expect($this->testUser->hasRole($enum4))->toBeFalse();\n    expect($this->testUser->hasRole('user-manager'))->toBeFalse();\n    expect($this->testUser->hasRole('writer'))->toBeFalse();\n    expect($this->testUser->hasRole('casted_enum-1'))->toBeFalse();\n    expect($this->testUser->hasRole('casted_enum-2'))->toBeFalse();\n\n    $this->testUser->assignRole($enum1);\n    $this->testUser->assignRole($enum2);\n    $this->testUser->assignRole($enum3);\n    $this->testUser->assignRole($enum4);\n\n    expect($this->testUser->hasRole($enum1))->toBeTrue();\n    expect($this->testUser->hasRole($enum2))->toBeTrue();\n    expect($this->testUser->hasRole($enum3))->toBeTrue();\n    expect($this->testUser->hasRole($enum4))->toBeTrue();\n\n    expect($this->testUser->hasRole([$enum1, 'writer']))->toBeTrue();\n    expect($this->testUser->hasRole([$enum3, 'casted_enum-2']))->toBeTrue();\n\n    expect($this->testUser->hasAllRoles([$enum1, $enum2, $enum3, $enum4]))->toBeTrue();\n    expect($this->testUser->hasAllRoles(['user-manager', 'writer', 'casted_enum-1', 'casted_enum-2']))->toBeTrue();\n    expect($this->testUser->hasAllRoles([$enum1, $enum2, $enum3, $enum4, 'not exist']))->toBeFalse();\n    expect($this->testUser->hasAllRoles(['user-manager', 'writer', 'casted_enum-1', 'casted_enum-2', 'not exist']))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles([$enum4, $enum3, $enum2, $enum1]))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['user-manager', 'writer', 'casted_enum-1', 'casted_enum-2']))->toBeTrue();\n\n    $this->testUser->removeRole($enum1);\n\n    expect($this->testUser->hasRole($enum1))->toBeFalse();\n});\n\nit('can scope a role using enums', function () {\n    $enum1 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::UserManager;\n    $enum2 = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::Writer;\n    $role1 = app(Role::class)->findOrCreate($enum1->value, 'web');\n    $role2 = app(Role::class)->findOrCreate($enum2->value, 'web');\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n\n    // assign only one user to a role\n    $user2->assignRole($enum1);\n    expect($user2->hasRole($enum1))->toBeTrue();\n    expect($user2->hasRole($enum2))->toBeFalse();\n\n    $scopedUsers1 = User::role($enum1)->get();\n    $scopedUsers2 = User::role($enum2)->get();\n    $scopedUsers3 = User::withoutRole($enum2)->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(0);\n    expect($scopedUsers3->count())->toEqual(3);\n});\n\nit('can assign and remove a role', function () {\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    $this->testUser->removeRole('testRole');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n});\n\nit('removes a role and returns roles', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->assignRole('testRole2');\n\n    expect($this->testUser->hasRole(['testRole', 'testRole2']))->toBeTrue();\n\n    $roles = $this->testUser->removeRole('testRole');\n\n    expect($roles->hasRole('testRole'))->toBeFalse();\n\n    expect($roles->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can assign and remove a role on a permission', function () {\n    $this->testUserPermission->assignRole('testRole');\n\n    expect($this->testUserPermission->hasRole('testRole'))->toBeTrue();\n\n    $this->testUserPermission->removeRole('testRole');\n\n    expect($this->testUserPermission->hasRole('testRole'))->toBeFalse();\n});\n\nit('can assign and remove a role using an object', function () {\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeTrue();\n\n    $this->testUser->removeRole($this->testUserRole);\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeFalse();\n});\n\nit('can assign and remove a role using an id', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeTrue();\n\n    $this->testUser->removeRole($this->testUserRole->getKey());\n\n    expect($this->testUser->hasRole($this->testUserRole))->toBeFalse();\n});\n\nit('can assign and remove multiple roles at once', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey(), 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n\n    $this->testUser->removeRole($this->testUserRole->getKey(), 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n});\n\nit('can assign and remove multiple roles using an array', function () {\n    $this->testUser->assignRole([$this->testUserRole->getKey(), 'testRole2']);\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n\n    $this->testUser->removeRole([$this->testUserRole->getKey(), 'testRole2']);\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n});\n\nit('does not remove already associated roles when assigning new roles', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    $this->testUser->assignRole('testRole2');\n\n    expect($this->testUser->fresh()->hasRole('testRole'))->toBeTrue();\n});\n\nit('does not throw an exception when assigning a role that is already assigned', function () {\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    $this->testUser->assignRole($this->testUserRole->getKey());\n\n    expect($this->testUser->fresh()->hasRole('testRole'))->toBeTrue();\n});\n\nit('throws an exception when assigning a role that does not exist', function () {\n    expect(fn () => $this->testUser->assignRole('evil-emperor'))->toThrow(RoleDoesNotExist::class);\n});\n\nit('can only assign roles from the correct guard', function () {\n    expect(fn () => $this->testUser->assignRole('testAdminRole'))->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when assigning a role from a different guard', function () {\n    expect(fn () => $this->testUser->assignRole($this->testAdminRole))->toThrow(GuardDoesNotMatch::class);\n});\n\nit('ignores null roles when syncing', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->syncRoles('testRole2', null);\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can sync roles from a string', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->syncRoles('testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can sync roles from a string on a permission', function () {\n    $this->testUserPermission->assignRole('testRole');\n\n    $this->testUserPermission->syncRoles('testRole2');\n\n    expect($this->testUserPermission->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUserPermission->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can avoid sync duplicated roles', function () {\n    $this->testUser->syncRoles('testRole', 'testRole', 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can avoid detach on role that does not exist sync', function () {\n    $this->testUser->syncRoles('testRole');\n\n    expect(fn () => $this->testUser->syncRoles('role-does-not-exist'))->toThrow(RoleDoesNotExist::class);\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n    expect($this->testUser->hasRole('role-does-not-exist'))->toBeFalse();\n});\n\nit('can sync multiple roles', function () {\n    $this->testUser->syncRoles('testRole', 'testRole2');\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('can sync multiple roles from an array', function () {\n    $this->testUser->syncRoles(['testRole', 'testRole2']);\n\n    expect($this->testUser->hasRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeTrue();\n});\n\nit('will remove all roles when an empty array is passed to sync roles', function () {\n    $this->testUser->assignRole('testRole');\n\n    $this->testUser->assignRole('testRole2');\n\n    $this->testUser->syncRoles([]);\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n});\n\nit('sync roles error does not detach roles', function () {\n    $this->testUser->assignRole('testRole');\n\n    expect(fn () => $this->testUser->syncRoles('testRole2', 'role-that-does-not-exist'))->toThrow(RoleDoesNotExist::class);\n\n    expect($this->testUser->fresh()->hasRole('testRole'))->toBeTrue();\n});\n\nit('will sync roles to a model that is not persisted', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncRoles([$this->testUserRole]);\n    $user->save();\n    $user->save(); // test save same model twice\n\n    expect($user->hasRole($this->testUserRole))->toBeTrue();\n\n    $user->syncRoles([$this->testUserRole]);\n    expect($user->hasRole($this->testUserRole))->toBeTrue();\n    expect($user->fresh()->hasRole($this->testUserRole))->toBeTrue();\n});\n\nit('does not run unnecessary sqls when assigning new roles', function () {\n    $role2 = app(Role::class)->where('name', ['testRole2'])->first();\n\n    DB::enableQueryLog();\n    $this->testUser->syncRoles($this->testUserRole, $role2);\n    DB::disableQueryLog();\n\n    $necessaryQueriesCount = 2;\n\n    // Teams reloads relation, adding an extra query\n    if (app(PermissionRegistrar::class)->teams) {\n        $necessaryQueriesCount++;\n    }\n\n    expect(DB::getQueryLog())->toHaveCount($necessaryQueriesCount);\n});\n\nit('calling syncRoles before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->syncRoles('testRole');\n    $user->save();\n\n    $user2 = new User(['email' => 'admin@user.com']);\n    $user2->syncRoles('testRole2');\n\n    DB::enableQueryLog();\n    $user2->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasRole('testRole'))->toBeTrue();\n    expect($user->fresh()->hasRole('testRole2'))->toBeFalse();\n\n    expect($user2->fresh()->hasRole('testRole2'))->toBeTrue();\n    expect($user2->fresh()->hasRole('testRole'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('calling assignRole before saving object doesnt interfere with other objects', function () {\n    $user = new User(['email' => 'test@user.com']);\n    $user->assignRole('testRole');\n    $user->save();\n\n    $admin_user = new User(['email' => 'admin@user.com']);\n    $admin_user->assignRole('testRole2');\n\n    DB::enableQueryLog();\n    $admin_user->save();\n    DB::disableQueryLog();\n\n    expect($user->fresh()->hasRole('testRole'))->toBeTrue();\n    expect($user->fresh()->hasRole('testRole2'))->toBeFalse();\n\n    expect($admin_user->fresh()->hasRole('testRole2'))->toBeTrue();\n    expect($admin_user->fresh()->hasRole('testRole'))->toBeFalse();\n    expect(count(DB::getQueryLog()))->toBe(2); // avoid unnecessary sync\n});\n\nit('throws an exception when syncing a role from another guard', function () {\n    expect(fn () => $this->testUser->syncRoles('testRole', 'testAdminRole'))->toThrow(RoleDoesNotExist::class);\n\n    expect(fn () => $this->testUser->syncRoles('testRole', $this->testAdminRole))->toThrow(GuardDoesNotMatch::class);\n});\n\nit('deletes pivot table entries when deleting models from HasRolesTest', function () {\n    $user = User::create(['email' => 'user@test.com']);\n\n    $user->assignRole('testRole');\n    $user->givePermissionTo('edit-articles');\n\n    $this->assertDatabaseHas('model_has_permissions', [config('permission.column_names.model_morph_key') => $user->id]);\n    $this->assertDatabaseHas('model_has_roles', [config('permission.column_names.model_morph_key') => $user->id]);\n\n    $user->delete();\n\n    $this->assertDatabaseMissing('model_has_permissions', [config('permission.column_names.model_morph_key') => $user->id]);\n    $this->assertDatabaseMissing('model_has_roles', [config('permission.column_names.model_morph_key') => $user->id]);\n});\n\nit('can scope users using a string', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n\n    $scopedUsers = User::role('testRole')->get();\n\n    expect($scopedUsers->count())->toEqual(1);\n});\n\nit('can withoutscope users using a string', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers = User::withoutRole('testRole2')->get();\n\n    expect($scopedUsers->count())->toEqual(1);\n});\n\nit('can scope users using an array', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role([$this->testUserRole])->get();\n    $scopedUsers2 = User::role(['testRole', 'testRole2'])->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(2);\n});\n\nit('can withoutscope users using an array', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole([$this->testUserRole])->get();\n    $scopedUsers2 = User::withoutRole([$this->testUserRole->name, 'testRole2'])->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(0);\n});\n\nit('can scope users using an array of ids and names', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $firstAssignedRoleName = $this->testUserRole->name;\n    $secondAssignedRoleId = app(Role::class)->findByName('testRole2')->getKey();\n\n    $scopedUsers = User::role([$firstAssignedRoleName, $secondAssignedRoleId])->get();\n\n    expect($scopedUsers->count())->toEqual(2);\n});\n\nit('can withoutscope users using an array of ids and names', function () {\n    app(Role::class)->create(['name' => 'testRole3']);\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $firstAssignedRoleName = $this->testUserRole->name;\n    $unassignedRoleId = app(Role::class)->findByName('testRole3')->getKey();\n\n    $scopedUsers = User::withoutRole([$firstAssignedRoleName, $unassignedRoleId])->get();\n\n    expect($scopedUsers->count())->toEqual(2);\n});\n\nit('can scope users using a collection', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role([$this->testUserRole])->get();\n    $scopedUsers2 = User::role(collect(['testRole', 'testRole2']))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(2);\n});\n\nit('can withoutscope users using a collection', function () {\n    app(Role::class)->create(['name' => 'testRole3']);\n\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole([$this->testUserRole])->get();\n    $scopedUsers2 = User::withoutRole(collect(['testRole', 'testRole3']))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(1);\n});\n\nit('can scope users using an object', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role($this->testUserRole)->get();\n    $scopedUsers2 = User::role([$this->testUserRole])->get();\n    $scopedUsers3 = User::role(collect([$this->testUserRole]))->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(1);\n});\n\nit('can withoutscope users using an object', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole($this->testUserRole)->get();\n    $scopedUsers2 = User::withoutRole([$this->testUserRole])->get();\n    $scopedUsers3 = User::withoutRole(collect([$this->testUserRole]))->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n    expect($scopedUsers2->count())->toEqual(2);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('can scope against a specific guard', function () {\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n\n    $scopedUsers1 = User::role('testRole', 'web')->get();\n\n    expect($scopedUsers1->count())->toEqual(1);\n\n    $user3 = Admin::create(['email' => 'user3@test.com']);\n    $user4 = Admin::create(['email' => 'user4@test.com']);\n    $user5 = Admin::create(['email' => 'user5@test.com']);\n    $testAdminRole2 = app(Role::class)->create(['name' => 'testAdminRole2', 'guard_name' => 'admin']);\n    $user3->assignRole($this->testAdminRole);\n    $user4->assignRole($this->testAdminRole);\n    $user5->assignRole($testAdminRole2);\n    $scopedUsers2 = Admin::role('testAdminRole', 'admin')->get();\n    $scopedUsers3 = Admin::role('testAdminRole2', 'admin')->get();\n\n    expect($scopedUsers2->count())->toEqual(2);\n    expect($scopedUsers3->count())->toEqual(1);\n});\n\nit('can withoutscope against a specific guard', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n    $user3 = User::create(['email' => 'user3@test.com']);\n    $user1->assignRole('testRole');\n    $user2->assignRole('testRole2');\n    $user3->assignRole('testRole2');\n\n    $scopedUsers1 = User::withoutRole('testRole', 'web')->get();\n\n    expect($scopedUsers1->count())->toEqual(2);\n\n    Admin::all()->each(fn ($item) => $item->delete());\n    $user4 = Admin::create(['email' => 'user4@test.com']);\n    $user5 = Admin::create(['email' => 'user5@test.com']);\n    $user6 = Admin::create(['email' => 'user6@test.com']);\n    $testAdminRole2 = app(Role::class)->create(['name' => 'testAdminRole2', 'guard_name' => 'admin']);\n    $user4->assignRole($this->testAdminRole);\n    $user5->assignRole($this->testAdminRole);\n    $user6->assignRole($testAdminRole2);\n    $scopedUsers2 = Admin::withoutRole('testAdminRole', 'admin')->get();\n    $scopedUsers3 = Admin::withoutRole('testAdminRole2', 'admin')->get();\n\n    expect($scopedUsers2->count())->toEqual(1);\n    expect($scopedUsers3->count())->toEqual(2);\n});\n\nit('throws an exception when trying to scope a role from another guard', function () {\n    expect(fn () => User::role('testAdminRole')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when trying to call withoutscope on a role from another guard', function () {\n    expect(fn () => User::withoutRole('testAdminRole')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when trying to scope a non existing role', function () {\n    expect(fn () => User::role('role not defined')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('throws an exception when trying to use withoutscope on a non existing role', function () {\n    expect(fn () => User::withoutRole('role not defined')->get())->toThrow(RoleDoesNotExist::class);\n});\n\nit('can determine that a user has one of the given roles', function () {\n    $roleModel = app(Role::class);\n\n    $roleModel->create(['name' => 'second role']);\n\n    expect($this->testUser->hasRole($roleModel->all()))->toBeFalse();\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasRole($roleModel->all()))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole($roleModel->all()))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole('testRole'))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole('role does not exist'))->toBeFalse();\n\n    expect($this->testUser->hasAnyRole(['testRole']))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole(['testRole', 'role does not exist']))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole(['role does not exist']))->toBeFalse();\n\n    expect($this->testUser->hasAnyRole('testRole', 'role does not exist'))->toBeTrue();\n});\n\nit('can determine that a user has all of the given roles', function () {\n    $roleModel = app(Role::class);\n\n    expect($this->testUser->hasAllRoles($roleModel->first()))->toBeFalse();\n\n    expect($this->testUser->hasAllRoles('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasAllRoles($roleModel->all()))->toBeFalse();\n\n    $roleModel->create(['name' => 'second role']);\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasAllRoles('testRole'))->toBeTrue();\n    expect($this->testUser->hasAllRoles('testRole', 'web'))->toBeTrue();\n    expect($this->testUser->hasAllRoles('testRole', 'fakeGuard'))->toBeFalse();\n\n    expect($this->testUser->hasAllRoles(['testRole', 'second role']))->toBeFalse();\n    expect($this->testUser->hasAllRoles(['testRole', 'second role'], 'web'))->toBeFalse();\n\n    $this->testUser->assignRole('second role');\n\n    expect($this->testUser->hasAllRoles(['testRole', 'second role']))->toBeTrue();\n    expect($this->testUser->hasAllRoles(['testRole', 'second role'], 'web'))->toBeTrue();\n    expect($this->testUser->hasAllRoles(['testRole', 'second role'], 'fakeGuard'))->toBeFalse();\n});\n\nit('can determine that a user has exact all of the given roles', function () {\n    $roleModel = app(Role::class);\n\n    expect($this->testUser->hasExactRoles($roleModel->first()))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles('testRole'))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles($roleModel->all()))->toBeFalse();\n\n    $roleModel->create(['name' => 'second role']);\n\n    $this->testUser->assignRole($this->testUserRole);\n\n    expect($this->testUser->hasExactRoles('testRole'))->toBeTrue();\n    expect($this->testUser->hasExactRoles('testRole', 'web'))->toBeTrue();\n    expect($this->testUser->hasExactRoles('testRole', 'fakeGuard'))->toBeFalse();\n\n    expect($this->testUser->hasExactRoles(['testRole', 'second role']))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'web'))->toBeFalse();\n\n    $this->testUser->assignRole('second role');\n\n    expect($this->testUser->hasExactRoles(['testRole', 'second role']))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'web'))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'fakeGuard'))->toBeFalse();\n\n    $roleModel->create(['name' => 'third role']);\n    $this->testUser->assignRole('third role');\n\n    expect($this->testUser->hasExactRoles(['testRole', 'second role']))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'web'))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role'], 'fakeGuard'))->toBeFalse();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role', 'third role']))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role', 'third role'], 'web'))->toBeTrue();\n    expect($this->testUser->hasExactRoles(['testRole', 'second role', 'third role'], 'fakeGuard'))->toBeFalse();\n});\n\nit('can determine that a user does not have a role from another guard', function () {\n    expect($this->testUser->hasRole('testAdminRole'))->toBeFalse();\n\n    expect($this->testUser->hasRole($this->testAdminRole))->toBeFalse();\n\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAnyRole(['testRole', 'testAdminRole']))->toBeTrue();\n\n    expect($this->testUser->hasAnyRole('testAdminRole', $this->testAdminRole))->toBeFalse();\n});\n\nit('can check against any multiple roles using multiple arguments', function () {\n    $this->testUser->assignRole('testRole');\n\n    expect($this->testUser->hasAnyRole($this->testAdminRole, ['testRole'], 'This Role Does Not Even Exist'))->toBeTrue();\n});\n\nit('returns false instead of an exception when checking against any undefined roles using multiple arguments', function () {\n    expect($this->testUser->hasAnyRole('This Role Does Not Even Exist', $this->testAdminRole))->toBeFalse();\n});\n\nit('throws an exception if an unsupported type is passed to hasRoles', function () {\n    expect(fn () => $this->testUser->hasRole(new class {}))->toThrow(\\TypeError::class);\n});\n\nit('can retrieve role names', function () {\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    expect($this->testUser->getRoleNames()->sort()->values())->toEqual(\n        collect(['testRole', 'testRole2'])\n    );\n});\n\nit('does not detach roles when user soft deleting', function () {\n    $user = SoftDeletingUser::create(['email' => 'test@example.com']);\n    $user->assignRole('testRole');\n    $user->delete();\n\n    $user = SoftDeletingUser::withTrashed()->find($user->id);\n\n    expect($user->hasRole('testRole'))->toBeTrue();\n});\n\nit('fires an event when a role is added', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->assignRole(['testRole', 'testRole2']);\n\n    $roleIds = app(Role::class)::whereIn('name', ['testRole', 'testRole2'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleAttachedEvent::class, function ($event) use ($roleIds) {\n        return $event->model instanceof User\n            && $event->model->hasRole('testRole')\n            && $event->model->hasRole('testRole2')\n            && $event->rolesOrIds === $roleIds;\n    });\n});\n\nit('fires an event when a role is removed', function () {\n    Event::fake();\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    $this->testUser->removeRole('testRole', 'testRole2');\n\n    $roleIds = app(Role::class)::whereIn('name', ['testRole', 'testRole2'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleDetachedEvent::class, function ($event) use ($roleIds) {\n        return $event->model instanceof User\n            && ! $event->model->hasRole('testRole')\n            && ! $event->model->hasRole('testRole2')\n            && $event->rolesOrIds === $roleIds;\n    });\n});\n\nit('can be given a role on permission when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    $testPermission = app(Permission::class)->with('roles')->get()->first();\n\n    $testPermission->assignRole('testRole');\n\n    expect($testPermission->hasRole('testRole'))->toBeTrue();\n});\n\nit('can be given a role on user when lazy loading is restricted', function () {\n    expect(Model::preventsLazyLoading())->toBeTrue();\n\n    User::create(['email' => 'other@user.com']);\n    $user = User::with('roles')->get()->first();\n    $user->assignRole('testRole');\n\n    expect($user->hasRole('testRole'))->toBeTrue();\n});\n\nit('fires detach event when syncing roles', function () {\n    Event::fake([RoleDetachedEvent::class, RoleAttachedEvent::class]);\n    app('config')->set('permission.events_enabled', true);\n\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    app(Role::class)->create(['name' => 'testRole3']);\n\n    $this->testUser->syncRoles('testRole3');\n\n    expect($this->testUser->hasRole('testRole'))->toBeFalse();\n    expect($this->testUser->hasRole('testRole2'))->toBeFalse();\n    expect($this->testUser->hasRole('testRole3'))->toBeTrue();\n\n    $removedRoleIds = app(Role::class)::whereIn('name', ['testRole', 'testRole2'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleDetachedEvent::class, function ($event) use ($removedRoleIds) {\n        return $event->model instanceof User\n            && ! $event->model->hasRole('testRole')\n            && ! $event->model->hasRole('testRole2')\n            && $event->rolesOrIds === $removedRoleIds;\n    });\n\n    $attachedRoleIds = app(Role::class)::whereIn('name', ['testRole3'])\n        ->pluck($this->testUserRole->getKeyName())\n        ->toArray();\n\n    Event::assertDispatched(RoleAttachedEvent::class, function ($event) use ($attachedRoleIds) {\n        return $event->model instanceof User\n            && $event->model->hasRole('testRole3')\n            && $event->rolesOrIds === $attachedRoleIds;\n    });\n});\n\n// ---- Team-specific tests ----\n\nit('deletes pivot table entries when deleting models', function () {\n    $user1 = User::create(['email' => 'user2@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n\n    setPermissionsTeamId(1);\n    $user1->assignRole('testRole');\n    $user1->givePermissionTo('edit-articles');\n    $user2->assignRole('testRole');\n    $user2->givePermissionTo('edit-articles');\n    setPermissionsTeamId(2);\n    $user1->givePermissionTo('edit-news');\n\n    $this->assertDatabaseHas('model_has_permissions', [config('permission.column_names.model_morph_key') => $user1->id]);\n    $this->assertDatabaseHas('model_has_roles', [config('permission.column_names.model_morph_key') => $user1->id]);\n\n    $user1->delete();\n\n    setPermissionsTeamId(1);\n    $this->assertDatabaseMissing('model_has_permissions', [config('permission.column_names.model_morph_key') => $user1->id]);\n    $this->assertDatabaseMissing('model_has_roles', [config('permission.column_names.model_morph_key') => $user1->id]);\n    $this->assertDatabaseHas('model_has_permissions', [config('permission.column_names.model_morph_key') => $user2->id]);\n    $this->assertDatabaseHas('model_has_roles', [config('permission.column_names.model_morph_key') => $user2->id]);\n});\n\nit('can assign same and different roles on same user different teams', function () {\n    app(Role::class)->create(['name' => 'testRole3']); // team_test_id = 1 by main class\n    app(Role::class)->create(['name' => 'testRole3', 'team_test_id' => 2]);\n    app(Role::class)->create(['name' => 'testRole4', 'team_test_id' => null]); // global role\n\n    $testRole3Team1 = app(Role::class)->where(['name' => 'testRole3', 'team_test_id' => 1])->first();\n    $testRole3Team2 = app(Role::class)->where(['name' => 'testRole3', 'team_test_id' => 2])->first();\n    $testRole4NoTeam = app(Role::class)->where(['name' => 'testRole4', 'team_test_id' => null])->first();\n    expect($testRole3Team1)->not->toBeNull();\n    expect($testRole4NoTeam)->not->toBeNull();\n\n    setPermissionsTeamId(1);\n    $this->testUser->assignRole('testRole', 'testRole2');\n\n    // explicit load of roles to assert no mismatch\n    // when same role assigned in diff teams\n    // while old team's roles are loaded\n    $this->testUser->load('roles');\n\n    setPermissionsTeamId(2);\n    $this->testUser->assignRole('testRole', 'testRole3');\n\n    setPermissionsTeamId(1);\n    $this->testUser->load('roles');\n\n    expect($this->testUser->getRoleNames()->sort()->values())\n        ->toEqual(collect(['testRole', 'testRole2']));\n    expect($this->testUser->hasExactRoles(['testRole', 'testRole2']))->toBeTrue();\n\n    $this->testUser->assignRole('testRole3', 'testRole4');\n    expect($this->testUser->hasExactRoles(['testRole', 'testRole2', 'testRole3', 'testRole4']))->toBeTrue();\n    expect($this->testUser->hasRole($testRole3Team1))->toBeTrue(); // testRole3 team=1\n    expect($this->testUser->hasRole($testRole4NoTeam))->toBeTrue(); // global role team=null\n\n    setPermissionsTeamId(2);\n    $this->testUser->load('roles');\n\n    expect($this->testUser->getRoleNames()->sort()->values())\n        ->toEqual(collect(['testRole', 'testRole3']));\n    expect($this->testUser->hasExactRoles(['testRole', 'testRole3']))->toBeTrue();\n    expect($this->testUser->hasRole($testRole3Team2))->toBeTrue(); // testRole3 team=2\n    $this->testUser->assignRole('testRole4');\n    expect($this->testUser->hasExactRoles(['testRole', 'testRole3', 'testRole4']))->toBeTrue();\n    expect($this->testUser->hasRole($testRole4NoTeam))->toBeTrue(); // global role team=null\n});\n\nit('can sync or remove roles without detach on different teams', function () {\n    app(Role::class)->create(['name' => 'testRole3', 'team_test_id' => 2]);\n\n    setPermissionsTeamId(1);\n    $this->testUser->syncRoles('testRole', 'testRole2');\n\n    setPermissionsTeamId(2);\n    $this->testUser->syncRoles('testRole', 'testRole3');\n\n    setPermissionsTeamId(1);\n    $this->testUser->load('roles');\n\n    expect($this->testUser->getRoleNames()->sort()->values())\n        ->toEqual(collect(['testRole', 'testRole2']));\n\n    $this->testUser->removeRole('testRole');\n    expect($this->testUser->getRoleNames()->sort()->values())\n        ->toEqual(collect(['testRole2']));\n\n    setPermissionsTeamId(2);\n    $this->testUser->load('roles');\n\n    expect($this->testUser->getRoleNames()->sort()->values())\n        ->toEqual(collect(['testRole', 'testRole3']));\n});\n\nit('can scope users on different teams', function () {\n    User::all()->each(fn ($item) => $item->delete());\n    $user1 = User::create(['email' => 'user1@test.com']);\n    $user2 = User::create(['email' => 'user2@test.com']);\n\n    setPermissionsTeamId(2);\n    $user1->assignRole($this->testUserRole);\n    $user2->assignRole('testRole2');\n\n    setPermissionsTeamId(1);\n    $user1->assignRole('testRole');\n\n    setPermissionsTeamId(2);\n    $scopedUsers1Team1 = User::role($this->testUserRole)->get();\n    $scopedUsers2Team1 = User::role(['testRole', 'testRole2'])->get();\n    $scopedUsers3Team1 = User::withoutRole('testRole')->get();\n\n    expect($scopedUsers1Team1->count())->toEqual(1);\n    expect($scopedUsers2Team1->count())->toEqual(2);\n    expect($scopedUsers3Team1->count())->toEqual(1);\n\n    setPermissionsTeamId(1);\n    $scopedUsers1Team2 = User::role($this->testUserRole)->get();\n    $scopedUsers2Team2 = User::role('testRole2')->get();\n    $scopedUsers3Team2 = User::withoutRole('testRole')->get();\n\n    expect($scopedUsers1Team2->count())->toEqual(1);\n    expect($scopedUsers2Team2->count())->toEqual(0);\n    expect($scopedUsers3Team2->count())->toEqual(1);\n});\n"
  },
  {
    "path": "tests/Traits/WildcardHasPermissionsTest.php",
    "content": "<?php\n\nuse Spatie\\Permission\\Exceptions\\PermissionDoesNotExist;\nuse Spatie\\Permission\\Exceptions\\WildcardPermissionInvalidArgument;\nuse Spatie\\Permission\\Exceptions\\WildcardPermissionNotProperlyFormatted;\nuse Spatie\\Permission\\Models\\Permission;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\User;\nuse Spatie\\Permission\\Tests\\TestSupport\\TestModels\\WildcardPermission;\n\nit('can check wildcard permission', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $permission1 = Permission::create(['name' => 'articles.edit,view,create']);\n    $permission2 = Permission::create(['name' => 'news.*']);\n    $permission3 = Permission::create(['name' => 'posts.*']);\n\n    $user1->givePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo('posts.create'))->toBeTrue();\n    expect($user1->hasPermissionTo('posts.create.123'))->toBeTrue();\n    expect($user1->hasPermissionTo('posts.*'))->toBeTrue();\n    expect($user1->hasPermissionTo('articles.view'))->toBeTrue();\n    expect($user1->hasPermissionTo('projects.view'))->toBeFalse();\n});\n\nit('can check wildcard permission for a non default guard', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $permission1 = Permission::create(['name' => 'articles.edit,view,create', 'guard_name' => 'api']);\n    $permission2 = Permission::create(['name' => 'news.*', 'guard_name' => 'api']);\n    $permission3 = Permission::create(['name' => 'posts.*', 'guard_name' => 'api']);\n\n    $user1->givePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo('posts.create', 'api'))->toBeTrue();\n    expect($user1->hasPermissionTo('posts.create.123', 'api'))->toBeTrue();\n    expect($user1->hasPermissionTo('posts.*', 'api'))->toBeTrue();\n    expect($user1->hasPermissionTo('articles.view', 'api'))->toBeTrue();\n    expect($user1->hasPermissionTo('projects.view', 'api'))->toBeFalse();\n});\n\nit('can check wildcard permission from instance without explicit guard argument', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $permission2 = Permission::create(['name' => 'articles.view']);\n    $permission1 = Permission::create(['name' => 'articles.edit', 'guard_name' => 'api']);\n    $permission3 = Permission::create(['name' => 'news.*', 'guard_name' => 'api']);\n    $permission4 = Permission::create(['name' => 'posts.*', 'guard_name' => 'api']);\n\n    $user1->givePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo($permission1))->toBeTrue();\n    expect($user1->hasPermissionTo($permission2))->toBeTrue();\n    expect($user1->hasPermissionTo($permission3))->toBeTrue();\n    expect($user1->hasPermissionTo($permission4))->toBeFalse();\n    expect($user1->hasPermissionTo('articles.edit'))->toBeFalse();\n});\n\nit('can assign wildcard permissions using enums', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $articlesCreator = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardArticlesCreator;\n    $newsEverything = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardNewsEverything;\n    $postsEverything = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardPostsEverything;\n    $postsCreate = Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardPostsCreate;\n\n    $permission1 = app(Permission::class)->findOrCreate($articlesCreator->value, 'web');\n    $permission2 = app(Permission::class)->findOrCreate($newsEverything->value, 'web');\n    $permission3 = app(Permission::class)->findOrCreate($postsEverything->value, 'web');\n\n    $user1->givePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo($postsCreate))->toBeTrue();\n    expect($user1->hasPermissionTo($postsCreate->value.'.123'))->toBeTrue();\n    expect($user1->hasPermissionTo($postsEverything))->toBeTrue();\n\n    expect($user1->hasPermissionTo(Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardArticlesView))->toBeTrue();\n    expect($user1->hasAnyPermission(Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardArticlesView))->toBeTrue();\n\n    expect($user1->hasPermissionTo(Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardProjectsView))->toBeFalse();\n\n    $user1->revokePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo(Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardPostsCreate))->toBeFalse();\n    expect($user1->hasPermissionTo($postsCreate->value.'.123'))->toBeFalse();\n    expect($user1->hasPermissionTo(Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardPostsEverything))->toBeFalse();\n\n    expect($user1->hasPermissionTo(Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardArticlesView))->toBeFalse();\n    expect($user1->hasAnyPermission(Spatie\\Permission\\Tests\\TestSupport\\TestModels\\TestRolePermissionsEnum::WildcardArticlesView))->toBeFalse();\n});\n\nit('can check wildcard permissions via roles', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $user1->assignRole('testRole');\n\n    $permission1 = Permission::create(['name' => 'articles,projects.edit,view,create']);\n    $permission2 = Permission::create(['name' => 'news.*.456']);\n    $permission3 = Permission::create(['name' => 'posts']);\n\n    $this->testUserRole->givePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo('posts.create'))->toBeTrue();\n    expect($user1->hasPermissionTo('news.create.456'))->toBeTrue();\n    expect($user1->hasPermissionTo('projects.create'))->toBeTrue();\n    expect($user1->hasPermissionTo('articles.view'))->toBeTrue();\n    expect($user1->hasPermissionTo('articles.list'))->toBeFalse();\n    expect($user1->hasPermissionTo('projects.list'))->toBeFalse();\n});\n\nit('clears wildcard index when assigning a role', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user = User::create(['email' => 'user1@test.com']);\n\n    $permission = Permission::create(['name' => 'posts.*']);\n    $this->testUserRole->givePermissionTo($permission);\n\n    // Check permission before assigning role — this populates the wildcard index\n    expect($user->hasPermissionTo('posts.create'))->toBeFalse();\n\n    $user->assignRole('testRole');\n\n    // After assigning the role, the wildcard index should be cleared\n    expect($user->hasPermissionTo('posts.create'))->toBeTrue();\n});\n\nit('clears wildcard index when removing a role', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user = User::create(['email' => 'user1@test.com']);\n\n    $permission = Permission::create(['name' => 'posts.*']);\n    $this->testUserRole->givePermissionTo($permission);\n\n    $user->assignRole('testRole');\n    expect($user->hasPermissionTo('posts.create'))->toBeTrue();\n\n    $user->removeRole('testRole');\n\n    // After removing the role, the wildcard index should be cleared\n    expect($user->hasPermissionTo('posts.create'))->toBeFalse();\n});\n\nit('can check custom wildcard permission', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n    app('config')->set('permission.wildcard_permission', WildcardPermission::class);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $permission1 = Permission::create(['name' => 'articles:edit;view;create']);\n    $permission2 = Permission::create(['name' => 'news:@']);\n    $permission3 = Permission::create(['name' => 'posts:@']);\n\n    $user1->givePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo('posts:create'))->toBeTrue();\n    expect($user1->hasPermissionTo('posts:create:123'))->toBeTrue();\n    expect($user1->hasPermissionTo('posts:@'))->toBeTrue();\n    expect($user1->hasPermissionTo('articles:view'))->toBeTrue();\n    expect($user1->hasPermissionTo('posts.*'))->toBeFalse();\n    expect($user1->hasPermissionTo('articles.view'))->toBeFalse();\n    expect($user1->hasPermissionTo('projects:view'))->toBeFalse();\n});\n\nit('can check custom wildcard permissions via roles', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n    app('config')->set('permission.wildcard_permission', WildcardPermission::class);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $user1->assignRole('testRole');\n\n    $permission1 = Permission::create(['name' => 'articles;projects:edit;view;create']);\n    $permission2 = Permission::create(['name' => 'news:@:456']);\n    $permission3 = Permission::create(['name' => 'posts']);\n\n    $this->testUserRole->givePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo('posts:create'))->toBeTrue();\n    expect($user1->hasPermissionTo('news:create:456'))->toBeTrue();\n    expect($user1->hasPermissionTo('projects:create'))->toBeTrue();\n    expect($user1->hasPermissionTo('articles:view'))->toBeTrue();\n    expect($user1->hasPermissionTo('news.create.456'))->toBeFalse();\n    expect($user1->hasPermissionTo('projects.create'))->toBeFalse();\n    expect($user1->hasPermissionTo('articles:list'))->toBeFalse();\n    expect($user1->hasPermissionTo('projects:list'))->toBeFalse();\n});\n\nit('can check non wildcard permissions', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $permission1 = Permission::create(['name' => 'edit articles']);\n    $permission2 = Permission::create(['name' => 'create news']);\n    $permission3 = Permission::create(['name' => 'update comments']);\n\n    $user1->givePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo('edit articles'))->toBeTrue();\n    expect($user1->hasPermissionTo('create news'))->toBeTrue();\n    expect($user1->hasPermissionTo('update comments'))->toBeTrue();\n});\n\nit('can verify complex wildcard permissions', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $permission1 = Permission::create(['name' => '*.create,update,delete.*.test,course,finance']);\n    $permission2 = Permission::create(['name' => 'papers,posts,projects,orders.*.test,test1,test2.*']);\n    $permission3 = Permission::create(['name' => 'User::class.create,edit,view']);\n\n    $user1->givePermissionTo([$permission1, $permission2, $permission3]);\n\n    expect($user1->hasPermissionTo('invoices.delete.367463.finance'))->toBeTrue();\n    expect($user1->hasPermissionTo('projects.update.test2.test3'))->toBeTrue();\n    expect($user1->hasPermissionTo('User::class.edit'))->toBeTrue();\n    expect($user1->hasPermissionTo('User::class.delete'))->toBeFalse();\n    expect($user1->hasPermissionTo('User::class.*'))->toBeFalse();\n});\n\nit('throws exception when wildcard permission is not properly formatted', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user1 = User::create(['email' => 'user1@test.com']);\n\n    $permission = Permission::create(['name' => '*..']);\n\n    $user1->givePermissionTo([$permission]);\n\n    expect(fn () => $user1->hasPermissionTo('invoices.*'))\n        ->toThrow(WildcardPermissionNotProperlyFormatted::class);\n});\n\nit('can verify permission instances not assigned to user', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user = User::create(['email' => 'user@test.com']);\n\n    $userPermission = Permission::create(['name' => 'posts.*']);\n    $permissionToVerify = Permission::create(['name' => 'posts.create']);\n\n    $user->givePermissionTo([$userPermission]);\n\n    expect($user->hasPermissionTo('posts.create'))->toBeTrue();\n    expect($user->hasPermissionTo('posts.create.123'))->toBeTrue();\n    expect($user->hasPermissionTo($permissionToVerify->id))->toBeTrue();\n    expect($user->hasPermissionTo($permissionToVerify))->toBeTrue();\n});\n\nit('can verify permission instances assigned to user', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user = User::create(['email' => 'user@test.com']);\n\n    $userPermission = Permission::create(['name' => 'posts.*']);\n    $permissionToVerify = Permission::create(['name' => 'posts.create']);\n\n    $user->givePermissionTo([$userPermission, $permissionToVerify]);\n\n    expect($user->hasPermissionTo('posts.create'))->toBeTrue();\n    expect($user->hasPermissionTo('posts.create.123'))->toBeTrue();\n    expect($user->hasPermissionTo($permissionToVerify))->toBeTrue();\n    expect($user->hasPermissionTo($userPermission))->toBeTrue();\n});\n\nit('can verify integers as strings', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user = User::create(['email' => 'user@test.com']);\n\n    $userPermission = Permission::create(['name' => '8']);\n\n    $user->givePermissionTo([$userPermission]);\n\n    expect($user->hasPermissionTo('8'))->toBeTrue();\n});\n\nit('throws exception when permission has invalid arguments', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user = User::create(['email' => 'user@test.com']);\n\n    expect(fn () => $user->hasPermissionTo(['posts.create']))\n        ->toThrow(WildcardPermissionInvalidArgument::class);\n});\n\nit('throws exception when permission id not exists', function () {\n    app('config')->set('permission.enable_wildcard_permission', true);\n\n    $user = User::create(['email' => 'user@test.com']);\n\n    expect(fn () => $user->hasPermissionTo(6))\n        ->toThrow(PermissionDoesNotExist::class);\n});\n"
  }
]