From b3ed86b6d0ae536a634240b55113e85a4e0e08d5 Mon Sep 17 00:00:00 2001 From: marco76tv Date: Tue, 9 Apr 2024 17:57:59 +0200 Subject: [PATCH] . --- .editorconfig | 30 +- .gitattributes | 38 +- .github/CONTRIBUTING.md | 110 +-- .github/FUNDING.yml | 2 +- .github/ISSUE_TEMPLATE/config.yml | 28 +- .github/SECURITY.md | 6 +- .github/dependabot.yml | 22 +- .github/workflows/dependabot-auto-merge.yml | 62 +- .../workflows/fix-php-code-style-issues.yml | 40 +- .github/workflows/phpstan.yml | 50 +- .github/workflows/run-tests.yml | 92 +-- .github/workflows/update-changelog.yml | 54 +- .gitignore | 44 +- .prettierrc | 10 +- CHANGELOG.md | 14 +- LICENSE.md | 42 +- README.md | 324 ++++----- composer.json | 158 ++-- package.json | 58 +- phpstan.neon.dist | 24 +- phpunit.xml.dist | 78 +- postcss.config.js | 12 +- src/Columns/ChildResourceLink.php | 148 ++-- ...FilamentNestedResourcesServiceProvider.php | 32 +- src/NestedEntry.php | 112 +-- src/NestedResource.php | 476 ++++++------ src/ResourcePages/NestedPage.php | 676 +++++++++--------- src/Table/Actions/LinkToChildrenAction.php | 72 +- tailwind.config.js | 42 +- tests/ExampleTest.php | 10 +- tests/Pest.php | 10 +- tests/TestCase.php | 80 +-- 32 files changed, 1478 insertions(+), 1478 deletions(-) diff --git a/.editorconfig b/.editorconfig index dd9a2b5..bb23509 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,15 +1,15 @@ -root = true - -[*] -charset = utf-8 -end_of_line = lf -indent_size = 4 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true - -[*.md] -trim_trailing_whitespace = false - -[*.{yml,yaml}] -indent_size = 2 +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes index 9e9519b..62d5d71 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,19 +1,19 @@ -# Path-based git attributes -# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html - -# Ignore all test and documentation with "export-ignore". -/.github export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore -/phpunit.xml.dist export-ignore -/art export-ignore -/docs export-ignore -/tests export-ignore -/.editorconfig export-ignore -/.php_cs.dist.php export-ignore -/psalm.xml export-ignore -/psalm.xml.dist export-ignore -/testbench.yaml export-ignore -/UPGRADING.md export-ignore -/phpstan.neon.dist export-ignore -/phpstan-baseline.neon export-ignore +# Path-based git attributes +# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html + +# Ignore all test and documentation with "export-ignore". +/.github export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/phpunit.xml.dist export-ignore +/art export-ignore +/docs export-ignore +/tests export-ignore +/.editorconfig export-ignore +/.php_cs.dist.php export-ignore +/psalm.xml export-ignore +/psalm.xml.dist export-ignore +/testbench.yaml export-ignore +/UPGRADING.md export-ignore +/phpstan.neon.dist export-ignore +/phpstan-baseline.neon export-ignore diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b4ae1c4..11a9e31 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,55 +1,55 @@ -# Contributing - -Contributions are **welcome** and will be fully **credited**. - -Please read and understand the contribution guide before creating an issue or pull request. - -## Etiquette - -This project is open source, and as such, the maintainers give their free time to build and maintain the source code -held within. They make the code freely available in the hope that it will be of use to other developers. It would be -extremely unfair for them to suffer abuse or anger for their hard work. - -Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the -world that developers are civilized and selfless people. - -It's the duty of the maintainer to ensure that all submissions to the project are of sufficient -quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. - -## Viability - -When requesting or submitting new features, first consider whether it might be useful to others. Open -source projects are used by many developers, who may have entirely different needs to your own. Think about -whether or not your feature is likely to be used by other users of the project. - -## Procedure - -Before filing an issue: - -- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. -- Check to make sure your feature suggestion isn't already present within the project. -- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. -- Check the pull requests tab to ensure that the feature isn't already in progress. - -Before submitting a pull request: - -- Check the codebase to ensure that your feature doesn't already exist. -- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. - -## Requirements - -If the project maintainer has any additional requirements, you will find them listed here. - -- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). - -- **Add tests!** - Your patch won't be accepted if it doesn't have tests. - -- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. - -- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. - -- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. - -- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. - -**Happy coding**! +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +Please read and understand the contribution guide before creating an issue or pull request. + +## Etiquette + +This project is open source, and as such, the maintainers give their free time to build and maintain the source code +held within. They make the code freely available in the hope that it will be of use to other developers. It would be +extremely unfair for them to suffer abuse or anger for their hard work. + +Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the +world that developers are civilized and selfless people. + +It's the duty of the maintainer to ensure that all submissions to the project are of sufficient +quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. + +## Viability + +When requesting or submitting new features, first consider whether it might be useful to others. Open +source projects are used by many developers, who may have entirely different needs to your own. Think about +whether or not your feature is likely to be used by other users of the project. + +## Procedure + +Before filing an issue: + +- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. +- Check to make sure your feature suggestion isn't already present within the project. +- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. +- Check the pull requests tab to ensure that the feature isn't already in progress. + +Before submitting a pull request: + +- Check the codebase to ensure that your feature doesn't already exist. +- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. + +## Requirements + +If the project maintainer has any additional requirements, you will find them listed here. + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. + +**Happy coding**! diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 4c00b48..8bd5c26 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -github: Sevendays-Digital +github: Sevendays-Digital diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 19cbee1..5b46200 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,14 +1,14 @@ -blank_issues_enabled: false -contact_links: - - name: Ask a question - url: https://github.com/Sevendays-Digital/filament-nested-resources/discussions/new?category=q-a - about: Ask the community for help - - name: Request a feature - url: https://github.com/Sevendays-Digital/filament-nested-resources/discussions/new?category=ideas - about: Share ideas for new features - - name: Report a security issue - url: https://github.com/Sevendays-Digital/filament-nested-resources/security/policy - about: Learn how to notify us for sensitive bugs - - name: Report a bug - url: https://github.com/Sevendays-Digital/filament-nested-resources/issues/new - about: Report a reproducable bug +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/Sevendays-Digital/filament-nested-resources/discussions/new?category=q-a + about: Ask the community for help + - name: Request a feature + url: https://github.com/Sevendays-Digital/filament-nested-resources/discussions/new?category=ideas + about: Share ideas for new features + - name: Report a security issue + url: https://github.com/Sevendays-Digital/filament-nested-resources/security/policy + about: Learn how to notify us for sensitive bugs + - name: Report a bug + url: https://github.com/Sevendays-Digital/filament-nested-resources/issues/new + about: Report a reproducable bug diff --git a/.github/SECURITY.md b/.github/SECURITY.md index b2295b1..abfd8b9 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1,3 +1,3 @@ -# Security Policy - -If you discover any security related issues, please email haringsrob@gmail.com instead of using the issue tracker. +# Security Policy + +If you discover any security related issues, please email haringsrob@gmail.com instead of using the issue tracker. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 30c8a49..9c0f9ee 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,12 +1,12 @@ -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - labels: +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: - "dependencies" \ No newline at end of file diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 78b1967..dcdc00a 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -1,32 +1,32 @@ -name: dependabot-auto-merge -on: pull_request_target - -permissions: - pull-requests: write - contents: write - -jobs: - dependabot: - runs-on: ubuntu-latest - if: ${{ github.actor == 'dependabot[bot]' }} - steps: - - - name: Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@v1.6.0 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - - name: Auto-merge Dependabot PRs for semver-minor updates - if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - - - name: Auto-merge Dependabot PRs for semver-patch updates - if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} +name: dependabot-auto-merge +on: pull_request_target + +permissions: + pull-requests: write + contents: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v1.6.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Auto-merge Dependabot PRs for semver-minor updates + if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge Dependabot PRs for semver-patch updates + if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file diff --git a/.github/workflows/fix-php-code-style-issues.yml b/.github/workflows/fix-php-code-style-issues.yml index 32507ff..842b196 100644 --- a/.github/workflows/fix-php-code-style-issues.yml +++ b/.github/workflows/fix-php-code-style-issues.yml @@ -1,21 +1,21 @@ -name: Fix PHP code style issues - -on: [push] - -jobs: - php-code-styling: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} - - - name: Fix PHP code style issues - uses: aglipanci/laravel-pint-action@2.2.0 - - - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v4 - with: +name: Fix PHP code style issues + +on: [push] + +jobs: + php-code-styling: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + + - name: Fix PHP code style issues + uses: aglipanci/laravel-pint-action@2.2.0 + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: commit_message: Fix styling \ No newline at end of file diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index e897c13..71c151f 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -1,26 +1,26 @@ -name: PHPStan - -on: - push: - paths: - - '**.php' - - 'phpstan.neon.dist' - -jobs: - phpstan: - name: phpstan - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.1' - coverage: none - - - name: Install composer dependencies - uses: ramsey/composer-install@v2 - - - name: Run PHPStan +name: PHPStan + +on: + push: + paths: + - '**.php' + - 'phpstan.neon.dist' + +jobs: + phpstan: + name: phpstan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + coverage: none + + - name: Install composer dependencies + uses: ramsey/composer-install@v2 + + - name: Run PHPStan run: ./vendor/bin/phpstan --error-format=github \ No newline at end of file diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3efc226..478ffbc 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,47 +1,47 @@ -name: run-tests - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - test: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: true - matrix: - os: [ubuntu-latest, windows-latest] - php: [8.1] - laravel: [9.*] - stability: [prefer-lowest, prefer-stable] - include: - - laravel: 9.* - testbench: 7.* - - name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo - coverage: none - - - name: Setup problem matchers - run: | - echo "::add-matcher::${{ runner.tool_cache }}/php.json" - echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - - - name: Install dependencies - run: | - composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update - composer update --${{ matrix.stability }} --prefer-dist --no-interaction - - - name: Execute tests +name: run-tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest, windows-latest] + php: [8.1] + laravel: [9.*] + stability: [prefer-lowest, prefer-stable] + include: + - laravel: 9.* + testbench: 7.* + + name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: none + + - name: Setup problem matchers + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Install dependencies + run: | + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update + composer update --${{ matrix.stability }} --prefer-dist --no-interaction + + - name: Execute tests run: vendor/bin/pest \ No newline at end of file diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml index 7787ca1..bc9c1f5 100644 --- a/.github/workflows/update-changelog.yml +++ b/.github/workflows/update-changelog.yml @@ -1,28 +1,28 @@ -name: "Update Changelog" - -on: - release: - types: [released] - -jobs: - update: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - ref: main - - - name: Update Changelog - uses: stefanzweifel/changelog-updater-action@v1 - with: - latest-version: ${{ github.event.release.name }} - release-notes: ${{ github.event.release.body }} - - - name: Commit updated CHANGELOG - uses: stefanzweifel/git-auto-commit-action@v4 - with: - branch: main - commit_message: Update CHANGELOG +name: "Update Changelog" + +on: + release: + types: [released] + +jobs: + update: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + ref: main + + - name: Update Changelog + uses: stefanzweifel/changelog-updater-action@v1 + with: + latest-version: ${{ github.event.release.name }} + release-notes: ${{ github.event.release.body }} + + - name: Commit updated CHANGELOG + uses: stefanzweifel/git-auto-commit-action@v4 + with: + branch: main + commit_message: Update CHANGELOG file_pattern: CHANGELOG.md \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6be158d..2d76ba9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,22 @@ -.idea -.phpunit.result.cache -build -composer.lock -coverage -docs -phpunit.xml -phpstan.neon -testbench.yaml -vendor -node_modules -.git-blame-ignore-revs -.php-cs-fixer.cache -*.phar -*.cache -.git-blame-ignore-revs -package-lock.json -.php-cs-fixer.cache -package-lock.json -.git-rewrite/ -.php-cs-fixer.cache -node_modules +.idea +.phpunit.result.cache +build +composer.lock +coverage +docs +phpunit.xml +phpstan.neon +testbench.yaml +vendor +node_modules +.git-blame-ignore-revs +.php-cs-fixer.cache +*.phar +*.cache +.git-blame-ignore-revs +package-lock.json +.php-cs-fixer.cache +package-lock.json +.git-rewrite/ +.php-cs-fixer.cache +node_modules diff --git a/.prettierrc b/.prettierrc index 98406c6..4ce5505 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,5 @@ -{ - "semi": false, - "singleQuote": true, - "trailingComma": "all" -} +{ + "semi": false, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 152d5a3..027076f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ -# Changelog - -All notable changes to `filament-nested-resources` will be documented in this file. - -## 1.0.0 - 202X-XX-XX - -- initial release +# Changelog + +All notable changes to `filament-nested-resources` will be documented in this file. + +## 1.0.0 - 202X-XX-XX + +- initial release diff --git a/LICENSE.md b/LICENSE.md index 07b7d1c..1efcb75 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +1,21 @@ -The MIT License (MIT) - -Copyright (c) Sevendays-Digital - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +The MIT License (MIT) + +Copyright (c) Sevendays-Digital + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 5b06f2a..1dce156 100644 --- a/README.md +++ b/README.md @@ -1,162 +1,162 @@ -# Helpers to work with nested resources - -This package adds support for nested resources in Filament. - -It provides the base classes and column to provide the nested structure. - -It currently is not that configurable and you need to follow naming conventions usually used by Laravel. - -Demo: - -https://user-images.githubusercontent.com/866743/230615762-af98d4ff-a285-4ad3-8964-bbb26b6d7a6e.mov - -## Installation - -You can install the package via composer: - -```bash -composer require sevendays-digital/filament-nested-resources -``` - -## Usage - -Currently you need to do a couple of changes to make this work. But you start of by creating a -[filament resource](https://filamentphp.com/docs/2.x/admin/resources/getting-started#creating-a-resource) -(The parent resource should already exist at this point. The resource we are changing is the child one). - -Once you have that, you will need to change the `Filament/Resources/ChildModelResource.php` to the `NestedResource`. - -```php -use SevendaysDigital\FilamentNestedResources\Columns\ChildResourceLink; -use SevendaysDigital\FilamentNestedResources\NestedResource; - -class ChildModelResource extends NestedResource -{ - public static function getParent(): string - { - return ParentModelResource::class; - } -} -``` - -Then for each of the resource pages, you need to add the trait: -```php -use SevendaysDigital\FilamentNestedResources\ResourcePages\NestedPage; -``` - -Finally, on your `ParentModelResource` you can add the column to provide the links: - -```php -public static function table(Table $table): Table -{ - return $table - ->columns([ - ChildResourceLink::make(ChildModelResource::class), - ]); -} -``` - -### Many To Many Relationships - -If you are using a many to many relationship, then you must write the inverse relationship inside your child model, -and then a special scope to retrieve it's parent. - -Example with MorphToMany relation: - -```php - -/* Category.php (parent) */ - -public function products(): MorphToMany -{ - $pivot_class = ProductMorph::class; - $pivot = app($pivot_class); - $pivot_table = $pivot->getTable(); - $pivot_fields = $pivot->getFillable(); - - - return $this->morphToMany(Product::class, 'model', $pivot_table) - ->using($pivot_class) - ->withPivot($pivot_fields) - ->withTimestamps(); -} - -/* Product.php (child) */ - -//inverse relation -public function categories(): MorphToMany -{ - $pivot_class = ProductMorph::class; - $pivot = app($pivot_class); - $pivot_table = $pivot->getTable(); - $pivot_fields = $pivot->getFillable(); - - - return $this->morphedByMany(Category::class, 'model', $pivot_table) - ->using($pivot_class) - ->withPivot($pivot_fields) - ->withTimestamps(); -} - -// This scope is very important to retreive the parent model -public function scopeOfCategory($query,$parent) -{ - return $query->whereHas('categories', function ($query) use($parent) { - $query->where('categories.id', $parent); - }); -} -``` - -### Accessing the parent - -When you need the parent in livewire context such as the form, you can add the second argument to your form method: - -```php -public static function form(Form $form, ?Event $parent = null): Form; -``` - -Where `Event` is the model that should be the parent. - -### Sidebar - -By default when in a "context" the sidebar will register the menu item for that resource. - -So if you are inside a Project which has documents, the sidebar will show documents when you are on a project or deeper -level. - -If you do not want this, you can set `shouldRegisterNavigationWhenInContext` to false in the child resource. - -### Notes - -Just make sure you set a custom slug for the resources so that it builds unique routes. - -https://filamentphp.com/docs/2.x/admin/resources/getting-started#customizing-the-url-slug - -## Testing - -There's none :). - -```bash -composer test -``` - -## Changelog - -Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. - -## Contributing - -Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. - -## Security Vulnerabilities - -Please review [our security policy](../../security/policy) on how to report security vulnerabilities. - -## Credits - -- [Harings Rob](https://github.com/Sevendays-Digital) -- [All Contributors](../../contributors) - -## License - -The MIT License (MIT). Please see [License File](LICENSE.md) for more information. +# Helpers to work with nested resources + +This package adds support for nested resources in Filament. + +It provides the base classes and column to provide the nested structure. + +It currently is not that configurable and you need to follow naming conventions usually used by Laravel. + +Demo: + +https://user-images.githubusercontent.com/866743/230615762-af98d4ff-a285-4ad3-8964-bbb26b6d7a6e.mov + +## Installation + +You can install the package via composer: + +```bash +composer require sevendays-digital/filament-nested-resources +``` + +## Usage + +Currently you need to do a couple of changes to make this work. But you start of by creating a +[filament resource](https://filamentphp.com/docs/2.x/admin/resources/getting-started#creating-a-resource) +(The parent resource should already exist at this point. The resource we are changing is the child one). + +Once you have that, you will need to change the `Filament/Resources/ChildModelResource.php` to the `NestedResource`. + +```php +use SevendaysDigital\FilamentNestedResources\Columns\ChildResourceLink; +use SevendaysDigital\FilamentNestedResources\NestedResource; + +class ChildModelResource extends NestedResource +{ + public static function getParent(): string + { + return ParentModelResource::class; + } +} +``` + +Then for each of the resource pages, you need to add the trait: +```php +use SevendaysDigital\FilamentNestedResources\ResourcePages\NestedPage; +``` + +Finally, on your `ParentModelResource` you can add the column to provide the links: + +```php +public static function table(Table $table): Table +{ + return $table + ->columns([ + ChildResourceLink::make(ChildModelResource::class), + ]); +} +``` + +### Many To Many Relationships + +If you are using a many to many relationship, then you must write the inverse relationship inside your child model, +and then a special scope to retrieve it's parent. + +Example with MorphToMany relation: + +```php + +/* Category.php (parent) */ + +public function products(): MorphToMany +{ + $pivot_class = ProductMorph::class; + $pivot = app($pivot_class); + $pivot_table = $pivot->getTable(); + $pivot_fields = $pivot->getFillable(); + + + return $this->morphToMany(Product::class, 'model', $pivot_table) + ->using($pivot_class) + ->withPivot($pivot_fields) + ->withTimestamps(); +} + +/* Product.php (child) */ + +//inverse relation +public function categories(): MorphToMany +{ + $pivot_class = ProductMorph::class; + $pivot = app($pivot_class); + $pivot_table = $pivot->getTable(); + $pivot_fields = $pivot->getFillable(); + + + return $this->morphedByMany(Category::class, 'model', $pivot_table) + ->using($pivot_class) + ->withPivot($pivot_fields) + ->withTimestamps(); +} + +// This scope is very important to retreive the parent model +public function scopeOfCategory($query,$parent) +{ + return $query->whereHas('categories', function ($query) use($parent) { + $query->where('categories.id', $parent); + }); +} +``` + +### Accessing the parent + +When you need the parent in livewire context such as the form, you can add the second argument to your form method: + +```php +public static function form(Form $form, ?Event $parent = null): Form; +``` + +Where `Event` is the model that should be the parent. + +### Sidebar + +By default when in a "context" the sidebar will register the menu item for that resource. + +So if you are inside a Project which has documents, the sidebar will show documents when you are on a project or deeper +level. + +If you do not want this, you can set `shouldRegisterNavigationWhenInContext` to false in the child resource. + +### Notes + +Just make sure you set a custom slug for the resources so that it builds unique routes. + +https://filamentphp.com/docs/2.x/admin/resources/getting-started#customizing-the-url-slug + +## Testing + +There's none :). + +```bash +composer test +``` + +## Changelog + +Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. + +## Contributing + +Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. + +## Security Vulnerabilities + +Please review [our security policy](../../security/policy) on how to report security vulnerabilities. + +## Credits + +- [Harings Rob](https://github.com/Sevendays-Digital) +- [All Contributors](../../contributors) + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/composer.json b/composer.json index 1b294bf..be16ee6 100644 --- a/composer.json +++ b/composer.json @@ -1,79 +1,79 @@ -{ - "name": "sevendays-digital/filament-nested-resources", - "description": "Helpers to work with nested resources", - "keywords": [ - "Sevendays-Digital", - "laravel", - "filament-nested-resources" - ], - "homepage": "https://github.com/sevendays-digital/filament-nested-resources", - "license": "MIT", - "authors": [ - { - "name": "Harings Rob", - "email": "haringsrob@gmail.com", - "role": "Developer" - } - ], - "require": { - "php": "^8.1", - "filament/filament": "^3.0", - "spatie/laravel-package-tools": "^1.13.5", - "illuminate/contracts": "^9.0|^10.0" - }, - "require-dev": { - "laravel/pint": "^1.0", - "nunomaduro/collision": "^6.0", - "larastan/larastan": "^2.0.1", - "orchestra/testbench": "^7.0", - "pestphp/pest": "^1.21", - "pestphp/pest-plugin-laravel": "^1.1", - "pestphp/pest-plugin-livewire": "^1.0", - "pestphp/pest-plugin-parallel": "^0.3", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5", - "spatie/laravel-ray": "^1.26" - }, - "autoload": { - "psr-4": { - "SevendaysDigital\\FilamentNestedResources\\": "src", - "SevendaysDigital\\FilamentNestedResources\\Database\\Factories\\": "database/factories" - } - }, - "autoload-dev": { - "psr-4": { - "SevendaysDigital\\FilamentNestedResources\\Tests\\": "tests" - } - }, - "scripts": { - "pint": "vendor/bin/pint", - "test:pest": "vendor/bin/pest --parallel", - "test:phpstan": "vendor/bin/phpstan analyse", - "test": [ - "@test:pest", - "@test:phpstan" - ] - }, - "config": { - "sort-packages": true, - "allow-plugins": { - "composer/package-versions-deprecated": true, - "pestphp/pest-plugin": true, - "phpstan/extension-installer": true - } - }, - "extra": { - "laravel": { - "providers": [ - "SevendaysDigital\\FilamentNestedResources\\FilamentNestedResourcesServiceProvider" - ], - "aliases": { - "FilamentNestedResources": "SevendaysDigital\\FilamentNestedResources\\Facades\\FilamentNestedResources" - } - } - }, - "minimum-stability": "dev", - "prefer-stable": true -} +{ + "name": "sevendays-digital/filament-nested-resources", + "description": "Helpers to work with nested resources", + "keywords": [ + "Sevendays-Digital", + "laravel", + "filament-nested-resources" + ], + "homepage": "https://github.com/sevendays-digital/filament-nested-resources", + "license": "MIT", + "authors": [ + { + "name": "Harings Rob", + "email": "haringsrob@gmail.com", + "role": "Developer" + } + ], + "require": { + "php": "^8.1", + "filament/filament": "^3.0", + "spatie/laravel-package-tools": "^1.13.5", + "illuminate/contracts": "^9.0|^10.0" + }, + "require-dev": { + "laravel/pint": "^1.0", + "nunomaduro/collision": "^6.0", + "larastan/larastan": "^2.0.1", + "orchestra/testbench": "^7.0", + "pestphp/pest": "^1.21", + "pestphp/pest-plugin-laravel": "^1.1", + "pestphp/pest-plugin-livewire": "^1.0", + "pestphp/pest-plugin-parallel": "^0.3", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5", + "spatie/laravel-ray": "^1.26" + }, + "autoload": { + "psr-4": { + "SevendaysDigital\\FilamentNestedResources\\": "src", + "SevendaysDigital\\FilamentNestedResources\\Database\\Factories\\": "database/factories" + } + }, + "autoload-dev": { + "psr-4": { + "SevendaysDigital\\FilamentNestedResources\\Tests\\": "tests" + } + }, + "scripts": { + "pint": "vendor/bin/pint", + "test:pest": "vendor/bin/pest --parallel", + "test:phpstan": "vendor/bin/phpstan analyse", + "test": [ + "@test:pest", + "@test:phpstan" + ] + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "composer/package-versions-deprecated": true, + "pestphp/pest-plugin": true, + "phpstan/extension-installer": true + } + }, + "extra": { + "laravel": { + "providers": [ + "SevendaysDigital\\FilamentNestedResources\\FilamentNestedResourcesServiceProvider" + ], + "aliases": { + "FilamentNestedResources": "SevendaysDigital\\FilamentNestedResources\\Facades\\FilamentNestedResources" + } + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/package.json b/package.json index f654598..ef2f1e6 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,29 @@ -{ - "private": true, - "scripts": { - "dev:styles": "npx tailwindcss -i resources/css/plugin.css -o resources/dist/filament-nested-resources.css --postcss --watch", - "dev:scripts": "esbuild resources/js/plugin.js --bundle --sourcemap=inline --outfile=resources/dist/filament-nested-resources.js --watch", - "build:styles": "npx tailwindcss -i resources/css/plugin.css -o resources/dist/filament-nested-resources.css --postcss --minify && npm run purge", - "build:scripts": "esbuild resources/js/plugin.js --bundle --minify --outfile=resources/dist/filament-nested-resources.js", - "purge": "filament-purge -i resources/dist/filament-nested-resources.css -o resources/dist/filament-nested-resources.css", - "dev": "npm-run-all --parallel dev:*", - "build": "npm-run-all build:*" - }, - "devDependencies": { - "@awcodes/filament-plugin-purge": "^1.0.2", - "@tailwindcss/forms": "^0.5.7", - "@tailwindcss/typography": "^0.5.10", - "autoprefixer": "^10.4.17", - "axios": "^1.6.7", - "esbuild": "^0.8.57", - "laravel-vite-plugin": "^1.0.1", - "lodash": "^4.17.21", - "npm-run-all": "^4.1.5", - "postcss": "^8.4.33", - "prettier": "^2.7.1", - "prettier-plugin-tailwindcss": "^0.1.13", - "tailwindcss": "^3.4.1", - "tippy.js": "^6.3.7", - "vite": "^5.0.12" - } -} +{ + "private": true, + "scripts": { + "dev:styles": "npx tailwindcss -i resources/css/plugin.css -o resources/dist/filament-nested-resources.css --postcss --watch", + "dev:scripts": "esbuild resources/js/plugin.js --bundle --sourcemap=inline --outfile=resources/dist/filament-nested-resources.js --watch", + "build:styles": "npx tailwindcss -i resources/css/plugin.css -o resources/dist/filament-nested-resources.css --postcss --minify && npm run purge", + "build:scripts": "esbuild resources/js/plugin.js --bundle --minify --outfile=resources/dist/filament-nested-resources.js", + "purge": "filament-purge -i resources/dist/filament-nested-resources.css -o resources/dist/filament-nested-resources.css", + "dev": "npm-run-all --parallel dev:*", + "build": "npm-run-all build:*" + }, + "devDependencies": { + "@awcodes/filament-plugin-purge": "^1.0.2", + "@tailwindcss/forms": "^0.5.7", + "@tailwindcss/typography": "^0.5.10", + "autoprefixer": "^10.4.17", + "axios": "^1.6.7", + "esbuild": "^0.8.57", + "laravel-vite-plugin": "^1.0.1", + "lodash": "^4.17.21", + "npm-run-all": "^4.1.5", + "postcss": "^8.4.33", + "prettier": "^2.7.1", + "prettier-plugin-tailwindcss": "^0.1.13", + "tailwindcss": "^3.4.1", + "tippy.js": "^6.3.7", + "vite": "^5.0.12" + } +} diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 9914a5e..3ab6726 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,12 +1,12 @@ -includes: - - phpstan-baseline.neon - -parameters: - level: 4 - paths: - - src - tmpDir: build/phpstan - checkOctaneCompatibility: true - checkModelProperties: true - checkMissingIterableValueType: false - +includes: + - phpstan-baseline.neon + +parameters: + level: 4 + paths: + - src + tmpDir: build/phpstan + checkOctaneCompatibility: true + checkModelProperties: true + checkMissingIterableValueType: false + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 34c4981..ea700b2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,39 +1,39 @@ - - - - - tests - - - - - ./src - - - - - - - - - - - + + + + + tests + + + + + ./src + + + + + + + + + + + diff --git a/postcss.config.js b/postcss.config.js index fef1b22..d0e6f8c 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,6 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/src/Columns/ChildResourceLink.php b/src/Columns/ChildResourceLink.php index c120404..b7c20d2 100644 --- a/src/Columns/ChildResourceLink.php +++ b/src/Columns/ChildResourceLink.php @@ -1,74 +1,74 @@ - - */ - private string $resourceClass; - - /** - * @param class-string $name - */ - public static function make(string $name): static - { - $item = parent::make($name); - $item->forResource($name); - $item->label($item->getChildLabelPlural()); - - return $item; - } - - public function getChildLabelPlural(): string - { - return Str::title($this->resourceClass::getPluralModelLabel()); - } - - public function getChildLabelSingular(): string - { - return Str::title($this->resourceClass::getModelLabel()); - } - - public function forResource(string $resourceClass): static - { - $this->resourceClass = $resourceClass; - - return $this; - } - - public function getState(): string - { - $count = $this->getCount(); - - return $count.' '.($count === 1 ? $this->getChildLabelSingular() : $this->getChildLabelPlural()); - } - - public function getUrl(): ?string - { - $baseParams = []; - if (property_exists($this->table->getLivewire(), 'urlParameters')) { - $baseParams = $this->table->getLivewire()->urlParameters; - } - - $param = Str::camel(Str::singular($this->resourceClass::getParent()::getSlug())); - - $params = $baseParams; - $params[$param] = $this->record->getKey(); - $url = $this->resourceClass::getUrl('index', $params); - - return $url; - } - - private function getCount(): int - { - return $this->resourceClass::getEloquentQuery($this->record->getKey())->count(); - } -} + + */ + private string $resourceClass; + + /** + * @param class-string $name + */ + public static function make(string $name): static + { + $item = parent::make($name); + $item->forResource($name); + $item->label($item->getChildLabelPlural()); + + return $item; + } + + public function getChildLabelPlural(): string + { + return Str::title($this->resourceClass::getPluralModelLabel()); + } + + public function getChildLabelSingular(): string + { + return Str::title($this->resourceClass::getModelLabel()); + } + + public function forResource(string $resourceClass): static + { + $this->resourceClass = $resourceClass; + + return $this; + } + + public function getState(): string + { + $count = $this->getCount(); + + return $count.' '.($count === 1 ? $this->getChildLabelSingular() : $this->getChildLabelPlural()); + } + + public function getUrl(): ?string + { + $baseParams = []; + if (property_exists($this->table->getLivewire(), 'urlParameters')) { + $baseParams = $this->table->getLivewire()->urlParameters; + } + + $param = Str::camel(Str::singular($this->resourceClass::getParent()::getSlug())); + + $params = $baseParams; + $params[$param] = $this->record->getKey(); + $url = $this->resourceClass::getUrl('index', $params); + + return $url; + } + + private function getCount(): int + { + return $this->resourceClass::getEloquentQuery($this->record->getKey())->count(); + } +} diff --git a/src/FilamentNestedResourcesServiceProvider.php b/src/FilamentNestedResourcesServiceProvider.php index 7bcf7a8..3cfb57b 100644 --- a/src/FilamentNestedResourcesServiceProvider.php +++ b/src/FilamentNestedResourcesServiceProvider.php @@ -1,16 +1,16 @@ -name(static::$name); - } -} +name(static::$name); + } +} diff --git a/src/NestedEntry.php b/src/NestedEntry.php index 46d5610..b336fd0 100644 --- a/src/NestedEntry.php +++ b/src/NestedEntry.php @@ -1,56 +1,56 @@ - $resource */ - public string $resource, - public string $label, - public null|string|int $id, - public array $urlParams, - ) { - } - - public function getListUrl(): string - { - $params = $this->urlParams; - array_pop($params); - - return $this->resource::getUrl('index', $params); - } - - public function getEditUrl(): string - { - $params = $this->urlParams; - array_pop($params); - - return $this->resource::getUrl('edit', [...$params, 'record' => $this->id()]); - } - - public function getRecord(): ?Model - { - return $this->resource::resolveRecordRouteBinding($this->id); - } - - public function getBreadcrumbTitle(): string - { - $res = $this->resource::getRecordTitle($this->resource::getModel()::find($this->id())); - if (is_string($res)) { - return $res; - } - - return ''; - } - - private function id(): string|int|null - { - return $this->id; - } -} + $resource */ + public string $resource, + public string $label, + public null|string|int $id, + public array $urlParams, + ) { + } + + public function getListUrl(): string + { + $params = $this->urlParams; + array_pop($params); + + return $this->resource::getUrl('index', $params); + } + + public function getEditUrl(): string + { + $params = $this->urlParams; + array_pop($params); + + return $this->resource::getUrl('edit', [...$params, 'record' => $this->id()]); + } + + public function getRecord(): ?Model + { + return $this->resource::resolveRecordRouteBinding($this->id); + } + + public function getBreadcrumbTitle(): string + { + $res = $this->resource::getRecordTitle($this->resource::getModel()::find($this->id())); + if (is_string($res)) { + return $res; + } + + return ''; + } + + private function id(): string|int|null + { + return $this->id; + } +} diff --git a/src/NestedResource.php b/src/NestedResource.php index 994a0e6..11f4f0f 100644 --- a/src/NestedResource.php +++ b/src/NestedResource.php @@ -1,238 +1,238 @@ - - */ - abstract public static function getParent(): string; - - public static function getParentAccessor(): string - { - return Str::of(static::getParent()::getModel()) - ->afterLast('\\Models\\') - ->camel() - ->toString(); - } - - public static function getParentId(): int|string|null - { - $parentId = Route::current()->parameter(static::getParentAccessor(), Route::current()->parameter('record')); - - return $parentId instanceof Model ? $parentId->getKey() : $parentId; - } - - public static function getEloquentQuery(string|int|null $parent = null): Builder - { - $query = parent::getEloquentQuery(); - $parentModel = static::getParent()::getModel(); - $key = (new $parentModel)->getKeyName(); - - $parentScope = 'of'.Str::studly(Str::afterLast(static::getParent()::getModel(), '\\')); - - if ($query->hasNamedScope($parentScope)) { - return $query->{$parentScope}($parent ?? static::getParentId()); - } - - $query->whereHas( - static::getParentAccessor(), - fn (Builder $builder) => $builder->where($key, '=', $parent ?? static::getParentId()) - ); - - return $query; - } - - public static function routes(\Filament\Panel $panel): void - { - $slug = static::getSlug(); - - $prefix = ''; - $parents = static::getParentTree(static::getParent()); - - foreach ($parents as $parent) { - $prefix .= $parent->urlPart.'/{'.$parent->urlPlaceholder.'}/'; - } - - $res = Route::name("$slug.") - ->prefix($prefix.$slug) - ->middleware(static::getMiddlewares()) - ->group(function () use ($panel) { - foreach (static::getPages() as $name => $page) { - // Route::get($page['route'], $page['class'])->name($name); - $page->registerRoute($panel)?->name($name); - } - }); - } - - public static function getMiddlewares(): string|array - { - return static::$middlewares; - } - - // public static function getUrl($name = 'index', $params = [], $isAbsolute = true): string - public static function getUrl(string $name = 'index', array $parameters = [], bool $isAbsolute = true, ?string $panel = null, ?Model $tenant = null): string - { - $params = $parameters; - - $list = static::getParentParametersForUrl(static::getParent(), $params); - - $params = [...$params, ...$list]; - - // Attempt to figure out what url binding should be set for the record. - $childParams = Route::current()->parameters(); - - if (isset($childParams['record'])) { - /** @var Page $controller */ - $controller = Route::current()->getController(); - /** @var resource $resource */ - $resource = $controller::getResource(); - $slug = Str::singular($resource::getSlug()); - $params[$slug] = $childParams['record']; - } - - $session_key = basename(static::class).'-'.$name.'-params'; - $session_url_params = Session::get($session_key); - if (! \is_array($session_url_params)) { - $session_url_params = []; - } - // $url_params = [...$session_url_params, ...$childParams, ...$params]; - $url_params = [...$childParams, ...$params]; - foreach ($url_params as $key => $value) { - if ($value === null && isset($session_url_params[$key])) { - $url_params[$key] = $session_url_params[$key]; - } - } - - Session::put($session_key, $url_params); - try { - $url = parent::getUrl($name, $url_params, $isAbsolute, $panel, $tenant); - } catch (\Exception $e) { - /* - dd([ - 'e' => $e->getMessage(), - 'name' => $name, - 'url_params' => $url_params, - 'session_url_params' => $session_url_params, - 'childParams' => $childParams, - 'params' => $params, - ]); - // */ - $url = '#'; - } - - return $url; - } - - /** - * @param class-string $parent - * @return NestedEntry[] - */ - public static function getParentTree(string $parent, array $urlParams = []): array - { - $singularSlug = Str::camel(Str::singular($parent::getSlug())); - - $list = []; - // if (new $parent() instanceof NestedResource) { - if (method_exists($parent, 'getParent')) { - $list = [...$list, ...static::getParentTree($parent::getParent(), $urlParams)]; - } - - $urlParams = static::getParentParametersForUrl($parent, $urlParams); - - $id = Route::current()?->parameter( - $singularSlug, - $urlParams[$singularSlug] ?? null - ); - - if ($id instanceof Model) { - $id = $id->getKey(); - } - - $list[$parent::getSlug()] = new NestedEntry( - urlPlaceholder: Str::camel(Str::singular($parent::getSlug())), - urlPart: $parent::getSlug(), - resource: $parent, - label: $parent::getPluralModelLabel(), - id: $id, - urlParams: $urlParams - ); - - return $list; - } - - /** - * @param class-string $parent - */ - public static function getParentParametersForUrl(string $parent, array $urlParameters = []): array - { - $list = []; - - $singularSlug = Str::camel(Str::singular($parent::getSlug())); - // if (new $parent() instanceof NestedResource) { - if (method_exists($parent, 'getParent')) { - $list = static::getParentParametersForUrl($parent::getParent(), $urlParameters); - } - $list[$singularSlug] = Route::current()?->parameter( - $singularSlug, - $urlParameters[$singularSlug] ?? null - ); - // dddx(['singularSlug'=>$singularSlug,'r'=>Route::current()]); - foreach ($list as $key => $value) { - if ($value instanceof Model) { - $list[$key] = $value->getKey(); - } - } - - return $list; - } - - public static function getNavigationGroup(): ?string - { - if (static::getParentId()) {// not work with morph - return static::getParent()::getRecordTitle( - static::getParent()::getModel()::find( - static::getParentId() - ) - ); - } - - return static::getParent()::getModelLabel(); - } - - public static function shouldRegisterNavigation(): bool - { - if (static::$shouldRegisterNavigationWhenInContext) { - try { - $url = static::getUrl('index'); - if ($url === '#') { - return false; - } - - return true; - } catch (UrlGenerationException) { - return false; - } - } - - return parent::shouldRegisterNavigation(); - } -} + + */ + abstract public static function getParent(): string; + + public static function getParentAccessor(): string + { + return Str::of(static::getParent()::getModel()) + ->afterLast('\\Models\\') + ->camel() + ->toString(); + } + + public static function getParentId(): int|string|null + { + $parentId = Route::current()->parameter(static::getParentAccessor(), Route::current()->parameter('record')); + + return $parentId instanceof Model ? $parentId->getKey() : $parentId; + } + + public static function getEloquentQuery(string|int|null $parent = null): Builder + { + $query = parent::getEloquentQuery(); + $parentModel = static::getParent()::getModel(); + $key = (new $parentModel)->getKeyName(); + + $parentScope = 'of'.Str::studly(Str::afterLast(static::getParent()::getModel(), '\\')); + + if ($query->hasNamedScope($parentScope)) { + return $query->{$parentScope}($parent ?? static::getParentId()); + } + + $query->whereHas( + static::getParentAccessor(), + fn (Builder $builder) => $builder->where($key, '=', $parent ?? static::getParentId()) + ); + + return $query; + } + + public static function routes(\Filament\Panel $panel): void + { + $slug = static::getSlug(); + + $prefix = ''; + $parents = static::getParentTree(static::getParent()); + + foreach ($parents as $parent) { + $prefix .= $parent->urlPart.'/{'.$parent->urlPlaceholder.'}/'; + } + + $res = Route::name("$slug.") + ->prefix($prefix.$slug) + ->middleware(static::getMiddlewares()) + ->group(function () use ($panel) { + foreach (static::getPages() as $name => $page) { + // Route::get($page['route'], $page['class'])->name($name); + $page->registerRoute($panel)?->name($name); + } + }); + } + + public static function getMiddlewares(): string|array + { + return static::$middlewares; + } + + // public static function getUrl($name = 'index', $params = [], $isAbsolute = true): string + public static function getUrl(string $name = 'index', array $parameters = [], bool $isAbsolute = true, ?string $panel = null, ?Model $tenant = null): string + { + $params = $parameters; + + $list = static::getParentParametersForUrl(static::getParent(), $params); + + $params = [...$params, ...$list]; + + // Attempt to figure out what url binding should be set for the record. + $childParams = Route::current()->parameters(); + + if (isset($childParams['record'])) { + /** @var Page $controller */ + $controller = Route::current()->getController(); + /** @var resource $resource */ + $resource = $controller::getResource(); + $slug = Str::singular($resource::getSlug()); + $params[$slug] = $childParams['record']; + } + + $session_key = basename(static::class).'-'.$name.'-params'; + $session_url_params = Session::get($session_key); + if (! \is_array($session_url_params)) { + $session_url_params = []; + } + // $url_params = [...$session_url_params, ...$childParams, ...$params]; + $url_params = [...$childParams, ...$params]; + foreach ($url_params as $key => $value) { + if ($value === null && isset($session_url_params[$key])) { + $url_params[$key] = $session_url_params[$key]; + } + } + + Session::put($session_key, $url_params); + try { + $url = parent::getUrl($name, $url_params, $isAbsolute, $panel, $tenant); + } catch (\Exception $e) { + /* + dd([ + 'e' => $e->getMessage(), + 'name' => $name, + 'url_params' => $url_params, + 'session_url_params' => $session_url_params, + 'childParams' => $childParams, + 'params' => $params, + ]); + // */ + $url = '#'; + } + + return $url; + } + + /** + * @param class-string $parent + * @return NestedEntry[] + */ + public static function getParentTree(string $parent, array $urlParams = []): array + { + $singularSlug = Str::camel(Str::singular($parent::getSlug())); + + $list = []; + // if (new $parent() instanceof NestedResource) { + if (method_exists($parent, 'getParent')) { + $list = [...$list, ...static::getParentTree($parent::getParent(), $urlParams)]; + } + + $urlParams = static::getParentParametersForUrl($parent, $urlParams); + + $id = Route::current()?->parameter( + $singularSlug, + $urlParams[$singularSlug] ?? null + ); + + if ($id instanceof Model) { + $id = $id->getKey(); + } + + $list[$parent::getSlug()] = new NestedEntry( + urlPlaceholder: Str::camel(Str::singular($parent::getSlug())), + urlPart: $parent::getSlug(), + resource: $parent, + label: $parent::getPluralModelLabel(), + id: $id, + urlParams: $urlParams + ); + + return $list; + } + + /** + * @param class-string $parent + */ + public static function getParentParametersForUrl(string $parent, array $urlParameters = []): array + { + $list = []; + + $singularSlug = Str::camel(Str::singular($parent::getSlug())); + // if (new $parent() instanceof NestedResource) { + if (method_exists($parent, 'getParent')) { + $list = static::getParentParametersForUrl($parent::getParent(), $urlParameters); + } + $list[$singularSlug] = Route::current()?->parameter( + $singularSlug, + $urlParameters[$singularSlug] ?? null + ); + // dddx(['singularSlug'=>$singularSlug,'r'=>Route::current()]); + foreach ($list as $key => $value) { + if ($value instanceof Model) { + $list[$key] = $value->getKey(); + } + } + + return $list; + } + + public static function getNavigationGroup(): ?string + { + if (static::getParentId()) {// not work with morph + return static::getParent()::getRecordTitle( + static::getParent()::getModel()::find( + static::getParentId() + ) + ); + } + + return static::getParent()::getModelLabel(); + } + + public static function shouldRegisterNavigation(): bool + { + if (static::$shouldRegisterNavigationWhenInContext) { + try { + $url = static::getUrl('index'); + if ($url === '#') { + return false; + } + + return true; + } catch (UrlGenerationException) { + return false; + } + } + + return parent::shouldRegisterNavigation(); + } +} diff --git a/src/ResourcePages/NestedPage.php b/src/ResourcePages/NestedPage.php index 08c4150..f98990d 100644 --- a/src/ResourcePages/NestedPage.php +++ b/src/ResourcePages/NestedPage.php @@ -1,338 +1,338 @@ - - */ - abstract public static function getResource(): string; - - public static function shouldRegisterNavigation(array $parameters = []): bool - { - return false; - } - - public function bootNestedPage(): void - { - if (empty($this->urlParameters)) { - $this->urlParameters = $this->getUrlParametersForState(); - } - } - - public function mountNestedPage(): void - { - if (empty($this->urlParameters)) { - $this->urlParameters = $this->getUrlParametersForState(); - } - } - - protected function getUrlParametersForState(): array - { - $parameters = Route::current()->parameters; - - foreach ($parameters as $key => $value) { - if ($value instanceof Model) { - $parameters[$key] = $value->getKey(); - } - } - - return $parameters; - } - - /** - * @return array - */ - public function getBreadcrumbs(): array - { - $resource = static::getResource(); - - // Build the nested breadcrumbs. - $nestedCrumbs = []; - foreach ($resource::getParentTree(static::getResource()::getParent(), $this->urlParameters) as $i => $nested) { - // Here we check if we can view and/or edit a record, if not we replace the link with a #. - // List. - if ($nested->resource::canViewAny()) { - $nestedCrumbs[$nested->getListUrl()] = $nested->resource::getBreadcrumb(); - } else { - $nestedCrumbs[] = $nested->resource::getBreadcrumb(); - } - - // Edit. - if (($record = $nested->getRecord()) && $nested->resource::canEdit($record)) { - $nestedCrumbs[$nested->getEditUrl()] = $nested->getBreadcrumbTitle(); - } else { - $nestedCrumbs[] = $nested->getBreadcrumbTitle(); - } - } - - // Add the current list entry. - if ($resource::canViewAny()) { - $currentListUrl = $resource::getUrl( - 'index', - $resource::getParentParametersForUrl($resource::getParent(), $this->urlParameters) - ); - $nestedCrumbs[$currentListUrl] = $resource::getBreadcrumb(); - } else { - $nestedCrumbs[] = $resource::getBreadcrumb(); - } - - // If it is a view page we need to add the current entry. - if ($this instanceof ViewRecord) { - if ($resource::canEdit($this->record)) { - $nestedCrumbs[$resource::getUrl('edit', $this->urlParameters)] = $this->getRecordTitle(); - } else { - $nestedCrumbs[] = $this->getTitle(); - } - } - - // Finalize with the current url. - $breadcrumb = $this->getBreadcrumb(); - if (filled($breadcrumb)) { - $nestedCrumbs[] = $breadcrumb; - } - - return $nestedCrumbs; - } - - protected function handleRecordCreation(array $data): Model - { - /** @var NestedResource $resource */ - $resource = $this::getResource(); - - $parentModelClass = $resource::getParent()::getModel(); - $parentId = $this->getParentId(); - $parentModel = $parentModelClass::find($parentId); - - $parent = Str::camel(Str::afterLast($parentModelClass, '\\')); - - // Create the model. - $model = $this->getModel()::make($data); - - $related = $model->{$parent}()->associate($parentModel); - $related->save(); - - return $model; - } - - protected function getTableQuery(): Builder - { - $urlParams = array_values($this->urlParameters); - $parameter = array_pop($urlParams); - - return static::getResource()::getEloquentQuery($parameter); - } - - protected function configureEditAction(PageEditAction|EditAction $action): void - { - $resource = static::getResource(); - - if ($action instanceof EditAction) { - $action - ->authorize(fn (Model $record): bool => $resource::canEdit($record)) - ->form(fn (): array => $this->getEditFormSchema()); - - if ($resource::hasPage('edit')) { - $action->url( - function (Model $record) use ($resource): string { - $params = $this->urlParameters; - $params['record'] = $record; - $url = $resource::getUrl('edit', $params); - - return $url; - } - ); - } - } else { - $action - ->authorize($resource::canEdit($this->getRecord())) - ->record($this->getRecord()) - ->recordTitle($this->getRecordTitle()); - - if ($resource::hasPage('edit')) { - $action->url(fn (): string => static::getResource()::getUrl( - 'edit', - [...$this->urlParameters, 'record' => $this->getRecord()] - )); - - return; - } - - $action->form($this->getFormSchema()); - } - } - - protected function configureCreateAction(CreateAction|\Filament\Tables\Actions\CreateAction $action): void - { - $resource = static::getResource(); - - $action - ->authorize($resource::canCreate()) - ->model($this->getModel()) - ->modelLabel($this->getModelLabel()) - ->form(fn (): array => $this->getCreateFormSchema()); - - if ($resource::hasPage('create')) { - $action->url(fn (): string => $resource::getUrl('create', $this->urlParameters)); - } - } - - protected function configureDeleteAction(DeleteAction|TableDeleteAction $action): void - { - $resource = static::getResource(); - /*-- WIP .. - $action - ->authorize($resource::canDelete($this->getRecord())) - ->record($this->getRecord()) - ->recordTitle($this->getRecordTitle()) - ->successRedirectUrl($resource::getUrl('index', $this->urlParameters)); - */ - } - - protected function configureViewAction(ViewAction|TableViewAction $action): void - { - $resource = static::getResource(); - - if ($action instanceof TableViewAction) { - $action - ->authorize(fn (Model $record): bool => $resource::canView($record)) - ->form(fn (): array => $this->getViewFormSchema()); - - if ($resource::hasPage('view')) { - $action->url(fn (Model $record): string => $resource::getUrl('view', [...$this->urlParameters, 'record' => $record])); - } - } else { - $action - ->authorize($resource::canView($this->getRecord())) - ->record($this->getRecord()) - ->recordTitle($this->getRecordTitle()); - - if ($resource::hasPage('view')) { - $action->url(fn (): string => static::getResource()::getUrl('view', [...$this->urlParameters, 'record' => $this->getRecord()])); - - return; - } - - $action->form($this->getFormSchema()); - } - } - - protected function getRedirectUrl(): string - { - $resource = static::getResource(); - - if ($resource::hasPage('view') && $resource::canView($this->record)) { - return $resource::getUrl('view', [...$this->urlParameters, 'record' => $this->record]); - } - - if ($resource::hasPage('edit') && $resource::canEdit($this->record)) { - return $resource::getUrl('edit', [...$this->urlParameters, 'record' => $this->record]); - } - - return $resource::getUrl('index', $this->urlParameters); - } - - protected function getParentId(): string|int - { - /** @var NestedResource $resource */ - $resource = $this::getResource(); - - $parent = Str::camel(Str::afterLast($resource::getParent()::getModel(), '\\')); - - if ($this->urlParameters[$parent] instanceof Model) { - return $this->urlParameters[$parent]->getKey(); - } - - if (\is_array($this->urlParameters[$parent]) && isset($this->urlParameters[$parent]['id'])) { - return $this->urlParameters[$parent]['id']; - } - - return $this->urlParameters[$parent]; - } - - public function getParent(): Model - { - $resource = $this::getResource(); - - return $resource::getParent()::getModel()::find($this->getParentId()); - } - - public function form(Form $form): Form - { - return static::getResource()::form($form, $this->getParent()); - } - /* WIP - protected function getTableRecordUrlUsing(): ?Closure - { - return function (Model $record): ?string { - foreach (['view', 'edit'] as $action) { - $action = $this->getCachedTableAction($action); - - if (! $action) { - continue; - } - - $action->record($record); - - if ($action->isHidden()) { - continue; - } - - $url = $action->getUrl(); - - if (! $url) { - continue; - } - - return $url; - } - - $resource = static::getResource(); - - foreach (['view', 'edit'] as $action) { - if (! $resource::hasPage($action)) { - continue; - } - - if (! $resource::{'can'.ucfirst($action)}($record)) { - continue; - } - - return $resource::getUrl($action, [...$this->urlParameters, 'record' => $record]); - } - - return null; - }; - } - */ -} + + */ + abstract public static function getResource(): string; + + public static function shouldRegisterNavigation(array $parameters = []): bool + { + return false; + } + + public function bootNestedPage(): void + { + if (empty($this->urlParameters)) { + $this->urlParameters = $this->getUrlParametersForState(); + } + } + + public function mountNestedPage(): void + { + if (empty($this->urlParameters)) { + $this->urlParameters = $this->getUrlParametersForState(); + } + } + + protected function getUrlParametersForState(): array + { + $parameters = Route::current()->parameters; + + foreach ($parameters as $key => $value) { + if ($value instanceof Model) { + $parameters[$key] = $value->getKey(); + } + } + + return $parameters; + } + + /** + * @return array + */ + public function getBreadcrumbs(): array + { + $resource = static::getResource(); + + // Build the nested breadcrumbs. + $nestedCrumbs = []; + foreach ($resource::getParentTree(static::getResource()::getParent(), $this->urlParameters) as $i => $nested) { + // Here we check if we can view and/or edit a record, if not we replace the link with a #. + // List. + if ($nested->resource::canViewAny()) { + $nestedCrumbs[$nested->getListUrl()] = $nested->resource::getBreadcrumb(); + } else { + $nestedCrumbs[] = $nested->resource::getBreadcrumb(); + } + + // Edit. + if (($record = $nested->getRecord()) && $nested->resource::canEdit($record)) { + $nestedCrumbs[$nested->getEditUrl()] = $nested->getBreadcrumbTitle(); + } else { + $nestedCrumbs[] = $nested->getBreadcrumbTitle(); + } + } + + // Add the current list entry. + if ($resource::canViewAny()) { + $currentListUrl = $resource::getUrl( + 'index', + $resource::getParentParametersForUrl($resource::getParent(), $this->urlParameters) + ); + $nestedCrumbs[$currentListUrl] = $resource::getBreadcrumb(); + } else { + $nestedCrumbs[] = $resource::getBreadcrumb(); + } + + // If it is a view page we need to add the current entry. + if ($this instanceof ViewRecord) { + if ($resource::canEdit($this->record)) { + $nestedCrumbs[$resource::getUrl('edit', $this->urlParameters)] = $this->getRecordTitle(); + } else { + $nestedCrumbs[] = $this->getTitle(); + } + } + + // Finalize with the current url. + $breadcrumb = $this->getBreadcrumb(); + if (filled($breadcrumb)) { + $nestedCrumbs[] = $breadcrumb; + } + + return $nestedCrumbs; + } + + protected function handleRecordCreation(array $data): Model + { + /** @var NestedResource $resource */ + $resource = $this::getResource(); + + $parentModelClass = $resource::getParent()::getModel(); + $parentId = $this->getParentId(); + $parentModel = $parentModelClass::find($parentId); + + $parent = Str::camel(Str::afterLast($parentModelClass, '\\')); + + // Create the model. + $model = $this->getModel()::make($data); + + $related = $model->{$parent}()->associate($parentModel); + $related->save(); + + return $model; + } + + protected function getTableQuery(): Builder + { + $urlParams = array_values($this->urlParameters); + $parameter = array_pop($urlParams); + + return static::getResource()::getEloquentQuery($parameter); + } + + protected function configureEditAction(PageEditAction|EditAction $action): void + { + $resource = static::getResource(); + + if ($action instanceof EditAction) { + $action + ->authorize(fn (Model $record): bool => $resource::canEdit($record)) + ->form(fn (): array => $this->getEditFormSchema()); + + if ($resource::hasPage('edit')) { + $action->url( + function (Model $record) use ($resource): string { + $params = $this->urlParameters; + $params['record'] = $record; + $url = $resource::getUrl('edit', $params); + + return $url; + } + ); + } + } else { + $action + ->authorize($resource::canEdit($this->getRecord())) + ->record($this->getRecord()) + ->recordTitle($this->getRecordTitle()); + + if ($resource::hasPage('edit')) { + $action->url(fn (): string => static::getResource()::getUrl( + 'edit', + [...$this->urlParameters, 'record' => $this->getRecord()] + )); + + return; + } + + $action->form($this->getFormSchema()); + } + } + + protected function configureCreateAction(CreateAction|\Filament\Tables\Actions\CreateAction $action): void + { + $resource = static::getResource(); + + $action + ->authorize($resource::canCreate()) + ->model($this->getModel()) + ->modelLabel($this->getModelLabel()) + ->form(fn (): array => $this->getCreateFormSchema()); + + if ($resource::hasPage('create')) { + $action->url(fn (): string => $resource::getUrl('create', $this->urlParameters)); + } + } + + protected function configureDeleteAction(DeleteAction|TableDeleteAction $action): void + { + $resource = static::getResource(); + /*-- WIP .. + $action + ->authorize($resource::canDelete($this->getRecord())) + ->record($this->getRecord()) + ->recordTitle($this->getRecordTitle()) + ->successRedirectUrl($resource::getUrl('index', $this->urlParameters)); + */ + } + + protected function configureViewAction(ViewAction|TableViewAction $action): void + { + $resource = static::getResource(); + + if ($action instanceof TableViewAction) { + $action + ->authorize(fn (Model $record): bool => $resource::canView($record)) + ->form(fn (): array => $this->getViewFormSchema()); + + if ($resource::hasPage('view')) { + $action->url(fn (Model $record): string => $resource::getUrl('view', [...$this->urlParameters, 'record' => $record])); + } + } else { + $action + ->authorize($resource::canView($this->getRecord())) + ->record($this->getRecord()) + ->recordTitle($this->getRecordTitle()); + + if ($resource::hasPage('view')) { + $action->url(fn (): string => static::getResource()::getUrl('view', [...$this->urlParameters, 'record' => $this->getRecord()])); + + return; + } + + $action->form($this->getFormSchema()); + } + } + + protected function getRedirectUrl(): string + { + $resource = static::getResource(); + + if ($resource::hasPage('view') && $resource::canView($this->record)) { + return $resource::getUrl('view', [...$this->urlParameters, 'record' => $this->record]); + } + + if ($resource::hasPage('edit') && $resource::canEdit($this->record)) { + return $resource::getUrl('edit', [...$this->urlParameters, 'record' => $this->record]); + } + + return $resource::getUrl('index', $this->urlParameters); + } + + protected function getParentId(): string|int + { + /** @var NestedResource $resource */ + $resource = $this::getResource(); + + $parent = Str::camel(Str::afterLast($resource::getParent()::getModel(), '\\')); + + if ($this->urlParameters[$parent] instanceof Model) { + return $this->urlParameters[$parent]->getKey(); + } + + if (\is_array($this->urlParameters[$parent]) && isset($this->urlParameters[$parent]['id'])) { + return $this->urlParameters[$parent]['id']; + } + + return $this->urlParameters[$parent]; + } + + public function getParent(): Model + { + $resource = $this::getResource(); + + return $resource::getParent()::getModel()::find($this->getParentId()); + } + + public function form(Form $form): Form + { + return static::getResource()::form($form, $this->getParent()); + } + /* WIP + protected function getTableRecordUrlUsing(): ?Closure + { + return function (Model $record): ?string { + foreach (['view', 'edit'] as $action) { + $action = $this->getCachedTableAction($action); + + if (! $action) { + continue; + } + + $action->record($record); + + if ($action->isHidden()) { + continue; + } + + $url = $action->getUrl(); + + if (! $url) { + continue; + } + + return $url; + } + + $resource = static::getResource(); + + foreach (['view', 'edit'] as $action) { + if (! $resource::hasPage($action)) { + continue; + } + + if (! $resource::{'can'.ucfirst($action)}($record)) { + continue; + } + + return $resource::getUrl($action, [...$this->urlParameters, 'record' => $record]); + } + + return null; + }; + } + */ +} diff --git a/src/Table/Actions/LinkToChildrenAction.php b/src/Table/Actions/LinkToChildrenAction.php index bcbb1c1..704915a 100644 --- a/src/Table/Actions/LinkToChildrenAction.php +++ b/src/Table/Actions/LinkToChildrenAction.php @@ -1,36 +1,36 @@ - */ - private string $childResource; - - public function forChildResource(string $childResource): self - { - Assert::classExists($childResource); - $this->childResource = $childResource; - - return $this; - } - - public function getUrl(): ?string - { - - //$parent = $this->getRecord()->{$this->getRecord()->getKeyName()}; - $parent = $this->getRecord()->getKey(); - - $params = [Str::camel(Str::singular($this->childResource::getParent()::getSlug())) => $parent]; - - return $this->childResource::getUrl( - 'index', - $this->childResource::getParentParametersForUrl($this->childResource, $params) - ); - } -} + */ + private string $childResource; + + public function forChildResource(string $childResource): self + { + Assert::classExists($childResource); + $this->childResource = $childResource; + + return $this; + } + + public function getUrl(): ?string + { + + //$parent = $this->getRecord()->{$this->getRecord()->getKeyName()}; + $parent = $this->getRecord()->getKey(); + + $params = [Str::camel(Str::singular($this->childResource::getParent()::getSlug())) => $parent]; + + return $this->childResource::getUrl( + 'index', + $this->childResource::getParentParametersForUrl($this->childResource, $params) + ); + } +} diff --git a/tailwind.config.js b/tailwind.config.js index 86a17ee..e1e8a61 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,21 +1,21 @@ -const colors = require('tailwindcss/colors') - -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: ['./resources/views/**/*.blade.php', './src/**/*.php'], - darkMode: 'class', - theme: { - extend: { - colors: { - danger: colors.rose, - primary: colors.amber, - success: colors.green, - warning: colors.amber, - }, - }, - }, - corePlugins: { - preflight: false, - }, - plugins: [], -} +const colors = require('tailwindcss/colors') + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./resources/views/**/*.blade.php', './src/**/*.php'], + darkMode: 'class', + theme: { + extend: { + colors: { + danger: colors.rose, + primary: colors.amber, + success: colors.green, + warning: colors.amber, + }, + }, + }, + corePlugins: { + preflight: false, + }, + plugins: [], +} diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php index 5d36321..4e4ab4a 100644 --- a/tests/ExampleTest.php +++ b/tests/ExampleTest.php @@ -1,5 +1,5 @@ -toBeTrue(); -}); +toBeTrue(); +}); diff --git a/tests/Pest.php b/tests/Pest.php index c79b10b..3aa8b6a 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,5 @@ -in(__DIR__); +in(__DIR__); diff --git a/tests/TestCase.php b/tests/TestCase.php index 9550c4a..ca871ef 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,40 +1,40 @@ - 'SevendaysDigital\\FilamentNestedResources\\Database\\Factories\\'.class_basename($modelName).'Factory' - ); - } - - protected function getPackageProviders($app) - { - return [ - LivewireServiceProvider::class, - FilamentServiceProvider::class, - FilamentNestedResourcesServiceProvider::class, - ]; - } - - public function getEnvironmentSetUp($app) - { - config()->set('database.default', 'testing'); - - /* - $migration = include __DIR__.'/../database/migrations/create_filament-nested-resources_table.php.stub'; - $migration->up(); - */ - } -} + 'SevendaysDigital\\FilamentNestedResources\\Database\\Factories\\'.class_basename($modelName).'Factory' + ); + } + + protected function getPackageProviders($app) + { + return [ + LivewireServiceProvider::class, + FilamentServiceProvider::class, + FilamentNestedResourcesServiceProvider::class, + ]; + } + + public function getEnvironmentSetUp($app) + { + config()->set('database.default', 'testing'); + + /* + $migration = include __DIR__.'/../database/migrations/create_filament-nested-resources_table.php.stub'; + $migration->up(); + */ + } +}