From a8297bc4bb1457cad74e88c9bc9884e09a9a2b74 Mon Sep 17 00:00:00 2001 From: riccardodallavia Date: Tue, 18 Jan 2022 10:49:15 +0100 Subject: [PATCH] first release --- .editorconfig | 15 ++ .gitattributes | 14 + .github/CONTRIBUTING.md | 55 ++++ .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/config.yml | 14 + .github/SECURITY.md | 3 + .github/dependabot.yml | 12 + .github/workflows/dependabot-auto-merge.yml | 32 +++ .github/workflows/php-cs-fixer.yml | 23 ++ .github/workflows/phpstan.yml | 26 ++ .github/workflows/run-tests.yml | 43 +++ .github/workflows/update-changelog.yml | 28 ++ .gitignore | 14 + .php_cs.dist.php | 40 +++ CHANGELOG.md | 3 + LICENSE.md | 21 ++ README.md | 283 ++++++++++++++++++++ composer.json | 70 +++++ config/helpers.php | 16 ++ phpstan-baseline.neon | 0 phpstan.neon.dist | 13 + phpunit.xml.dist | 39 +++ src/Helper.php | 81 ++++++ src/HelperServiceProvider.php | 16 ++ src/helpers.php | 12 + tests/HelpersTest.php | 106 ++++++++ tests/Models/Article.php | 15 ++ tests/Pest.php | 5 + tests/TestCase.php | 26 ++ 29 files changed, 1026 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/SECURITY.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dependabot-auto-merge.yml create mode 100644 .github/workflows/php-cs-fixer.yml create mode 100644 .github/workflows/phpstan.yml create mode 100644 .github/workflows/run-tests.yml create mode 100644 .github/workflows/update-changelog.yml create mode 100644 .gitignore create mode 100644 .php_cs.dist.php create mode 100644 CHANGELOG.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 composer.json create mode 100644 config/helpers.php create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon.dist create mode 100644 phpunit.xml.dist create mode 100644 src/Helper.php create mode 100644 src/HelperServiceProvider.php create mode 100644 src/helpers.php create mode 100644 tests/HelpersTest.php create mode 100644 tests/Models/Article.php create mode 100644 tests/Pest.php create mode 100644 tests/TestCase.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a7c44dd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..886475c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +# 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 +/tests export-ignore +/.editorconfig export-ignore +/.php_cs.dist export-ignore +/psalm.xml export-ignore +/psalm.xml.dist export-ignore +/testbench.yaml export-ignore diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..b4ae1c4 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +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**! diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6925ba6 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: maize-tech diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..ab492cb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/maize-tech/laravel-helpers/discussions/new?category=q-a + about: Ask the community for help + - name: Request a feature + url: https://github.com/maize-tech/laravel-helpers/discussions/new?category=ideas + about: Share ideas for new features + - name: Report a security issue + url: https://github.com/maize-tech/laravel-helpers/security/policy + about: Learn how to notify us for sensitive bugs + - name: Report a bug + url: https://github.com/maize-tech/laravel-helpers/issues/new + about: Report a reproducable bug diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..1512509 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +If you discover any security related issues, please email inn.security@h-farm.com instead of using the issue tracker. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..30c8a49 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +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: + - "dependencies" \ No newline at end of file diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..399ef82 --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +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.1.1 + 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}} diff --git a/.github/workflows/php-cs-fixer.yml b/.github/workflows/php-cs-fixer.yml new file mode 100644 index 0000000..f55d1fa --- /dev/null +++ b/.github/workflows/php-cs-fixer.yml @@ -0,0 +1,23 @@ +name: Check & fix styling + +on: [push] + +jobs: + php-cs-fixer: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Run PHP CS Fixer + uses: docker://oskarstark/php-cs-fixer-ga + with: + args: --config=.php_cs.dist.php --allow-risky=yes + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Fix styling diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml new file mode 100644 index 0000000..19d6402 --- /dev/null +++ b/.github/workflows/phpstan.yml @@ -0,0 +1,26 @@ +name: PHPStan + +on: + push: + paths: + - '**.php' + - 'phpstan.neon.dist' + +jobs: + phpstan: + name: phpstan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + coverage: none + + - name: Install composer dependencies + uses: ramsey/composer-install@v1 + + - name: Run PHPStan + run: ./vendor/bin/phpstan --error-format=github diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 0000000..161debc --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,43 @@ +name: run-tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest, windows-latest] + php: [8.0] + laravel: [8.*] + stability: [prefer-lowest, prefer-stable] + include: + - laravel: 8.* + testbench: ^6.23 + + name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - 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 diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml new file mode 100644 index 0000000..0e73b1e --- /dev/null +++ b/.github/workflows/update-changelog.yml @@ -0,0 +1,28 @@ +name: "Update Changelog" + +on: + release: + types: [released] + +jobs: + update: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + ref: master + + - 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: master + commit_message: Update CHANGELOG + file_pattern: CHANGELOG.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a43686 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.idea +.php_cs +.php_cs.cache +.phpunit.result.cache +build +composer.lock +coverage +docs +phpunit.xml +phpstan.neon +testbench.yaml +vendor +node_modules +.php-cs-fixer.cache diff --git a/.php_cs.dist.php b/.php_cs.dist.php new file mode 100644 index 0000000..8d8a790 --- /dev/null +++ b/.php_cs.dist.php @@ -0,0 +1,40 @@ +in([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +return (new PhpCsFixer\Config()) + ->setRules([ + '@PSR12' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => ['sort_algorithm' => 'alpha'], + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'trailing_comma_in_multiline' => true, + 'phpdoc_scalar' => true, + 'unary_operator_spaces' => true, + 'binary_operator_spaces' => true, + 'blank_line_before_statement' => [ + 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'class_attributes_separation' => [ + 'elements' => [ + 'method' => 'one', + ], + ], + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => true, + ], + 'single_trait_insert_per_statement' => true, + ]) + ->setFinder($finder); diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d569784 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +All notable changes to `laravel-helpers` will be documented in this file. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..cf7d937 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 MAIZE SRL + +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 new file mode 100644 index 0000000..e7071b2 --- /dev/null +++ b/README.md @@ -0,0 +1,283 @@ +# Laravel Helpers + +[![Latest Version on Packagist](https://img.shields.io/packagist/v/maize-tech/laravel-helpers.svg?style=flat-square)](https://packagist.org/packages/maize-tech/laravel-helpers) +[![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/maize-tech/laravel-helpers/run-tests?label=tests)](https://github.com/maize-tech/laravel-helpers/actions?query=workflow%3Arun-tests+branch%3Amain) +[![GitHub Code Style Action Status](https://img.shields.io/github/workflow/status/maize-tech/laravel-helpers/Check%20&%20fix%20styling?label=code%20style)](https://github.com/maize-tech/laravel-helpers/actions?query=workflow%3A"Check+%26+fix+styling"+branch%3Amain) +[![Total Downloads](https://img.shields.io/packagist/dt/maize-tech/laravel-helpers.svg?style=flat-square)](https://packagist.org/packages/maize-tech/laravel-helpers) + +This repository contains some useful helpers for most applications using Laravel. + +## Installation + +You can install the package via composer: + +```bash +composer require maize-tech/laravel-helpers +``` + +You can publish the config file with: +```bash +php artisan vendor:publish --tag="helpers-config" +``` + +This is the content of the published config file: + +```php +return [ + + /* + |-------------------------------------------------------------------------- + | Helper class + |-------------------------------------------------------------------------- + | + | Here you may specify the fully qualified class name of the helper class. + | + */ + + 'helper' => Maize\Helpers\Helper::class, + +]; +``` +## Usage + +To use the package, you can simply call the `hlp()` helper function, followed by one of the [Available methods](#available-methods) methods listed below. +If needed, you could also call the static method directly. + +Here's an example using both the helper function and the static method: + +```php +hlp()->sanitizeUrl('mywebsite.com'); // using the helper function + +\Maize\Helpers\Helper::sanitizeUrl('mywebsite.com'); // using the static method +``` + +## Available methods + +- [`paginationLimit`](#paginationlimit) +- [`anonymizeFilename`](#anonymizefilename) +- [`sanitizeUrl`](#sanitizeurl) +- [`instanceofTypes`](#instanceoftypes) +- [`classUsesTrait`](#classusestrait) +- [`morphClassOf`](#morphclassof) +- [`modelKeyName`](#modelkeyname) + +### `paginationLimit` + +The `paginationLimit` function returns the amount of items per page. + +It is useful when working with queries who need a pagination, and allows to define a default pagination limit and the max amount of items per page. + +It will also check whether the request's query string contains a `limit` parameter: if true, the given limit overrides the default limit. + +```php +use App\Models\Article; + +// use the default pagination limit (16 items) +// GET /api/articles +Article::paginate( + hlp()->paginationLimit() // returns 16 items +); + +// use the pagination limit given by the request query string +// GET /api/articles?limit=20 +Article::paginate( + hlp()->paginationLimit() // returns 20 items +); + +// provide a custom default pagination limit +// GET /api/articles +Article::paginate( + hlp()->paginationLimit(30) // returns 30 items +); + +// when defined, the request query string limit overrides the default limit +// GET /api/articles?limit=20 +Article::paginate( + hlp()->paginationLimit(30) // returns 20 items +); + +// provide a max limit of items for each page +// GET /api/articles?limit=200 +Article::paginate( + hlp()->paginationLimit(16, 50) // returns 50 items +); +``` + +### `anonymizeFilename` + +The `anonymizeFilename` function returns a randomized name of the given file followed by its extension. + +```php +string $filename = 'my-custom-file.xml'; + +// returns a UUID string followed by the file extension +// e.g. 'd437fd98-68d1-4874-b0e7-fac06e587083.xml' +hlp()->anonymizeFilename($filename); +``` + +### `sanitizeUrl` + +The `sanitizeUrl` function returns the lower case of the given url and prefixes it with the `https` protocol if none is set. + +```php +hlp()->sanitizeUrl('http://innovation.h-farm.com'); // returns 'http://innovation.h-farm.com' + +hlp()->sanitizeUrl('innovation.h-farm.com'); // returns 'https://innovation.h-farm.com' + +hlp()->sanitizeUrl('') // returns an empty string +``` + +### `instanceofTypes` + +The `instanceofTypes` function returns whether a class object is an instance of at least one of the given types or not. + +```php +use App\Models\User; +use Exception; +use Illuminate\Database\Eloquent\Model; + +$model = User::firstOrFail(); + +hlp()->instanceofTypes($model, Model::class); // returns true + +hlp()->instanceofTypes($model, Exception); // returns false + +hlp()->instanceofTypes($model, [ + Model::class, + Exception::class, +]); // returns true +``` + +### `classUsesTrait` + +The `classUsesTrait` function returns whether a class object or name uses the given trait or not. + +```php +use App\Models\User; +use Exception; +use Illuminate\Database\Eloquent\Factories\HasFactory; + +$model = User::firstOrFail(); + +hlp()->classUsesTrait(HasFactory::class, $model); // returns true + +hlp()->classUsesTrait(HasFactory::class, User::class); // returns true + +hlp()->classUsesTrait(Exception::class, $model); // returns false + +hlp()->classUsesTrait(Exception::class, User::class); // returns false +``` + +### `morphClassOf` + +The `morphClassOf` function returns the morph class name of a given model object or class name. + +```php +use App\Models\User; +use Illuminate\Database\Eloquent\Relations\Relation; + +$model = User::firstOrFail(); + +hlp()->morphClassOf($model); // returns 'App\Models\User' + +hlp()->morphClassOf(User::class); // returns 'App\Models\User' + +Relation::enforceMorphMap([ + 'user' => User::class, +]); + +hlp()->morphClassOf($model); // returns 'user' + +hlp()->morphClassOf(User::class); // returns 'user' +``` + +### `modelKeyName` + +The `modelKeyName` function returns the key name of a given model object or class name. + +```php +use App\Models\User; + +$model = User::firstOrFail(); + +hlp()->modelKeyName($model); // returns 'id' + +hlp()->modelKeyName(User::class); // returns 'id' +``` + +## Extending the Helper class + +If needed, you can easily extend the `Helper` class to add your own methods. + +All you have to do is create a new class which overrides the `Helper` base class and adds all the methods you need. +After that, you can override the `helper` attribute from `config/helpers.php` to match the newly created class. + +Here's an example of the output class: + +```php + Support\Helpers\Helper::class, + +]; +``` + +You can then call all your custom-made helper methods using the `hlp()` function: + +```php +hlp()->awesomeHelper(); // returns "Just another awesome helper!"; +``` + +## Testing + +```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 + +- [Enrico De Lazzari](https://github.com/enricodelazzari) +- [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 new file mode 100644 index 0000000..16d7704 --- /dev/null +++ b/composer.json @@ -0,0 +1,70 @@ +{ + "name": "maize-tech/laravel-helpers", + "description": "This is my package laravel-helpers", + "keywords": [ + "maize-tech", + "laravel", + "laravel-helpers" + ], + "homepage": "https://github.com/maize-tech/laravel-helpers", + "license": "MIT", + "authors": [ + { + "name": "Enrico De Lazzari", + "email": "enrico.delazzari@h-farm.com", + "homepage": "https://innovation.h-farm.com", + "role": "Developer" + } + ], + "require": { + "php": "^8.0", + "illuminate/database": "^8.0", + "illuminate/support": "^8.0", + "spatie/laravel-package-tools": "^1.9.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.4", + "nunomaduro/collision": "^5.10", + "nunomaduro/larastan": "^1.0", + "orchestra/testbench": "^6.22", + "pestphp/pest": "^1.21", + "pestphp/pest-plugin-laravel": "^1.1", + "pestphp/pest-plugin-mock": "^1.0", + "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": { + "Maize\\Helpers\\": "src" + }, + "files" : [ + "src/helpers.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Maize\\Helpers\\Tests\\": "tests" + } + }, + "scripts": { + "format": "vendor/bin/php-cs-fixer fix --config=.php_cs.dist.php --allow-risky=yes", + "analyse": "vendor/bin/phpstan analyse", + "test": "vendor/bin/pest", + "test-coverage": "vendor/bin/pest coverage" + }, + "config": { + "sort-packages": true + }, + "extra": { + "laravel": { + "providers": [ + "Maize\\Helpers\\HelperServiceProvider" + ] + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/config/helpers.php b/config/helpers.php new file mode 100644 index 0000000..c6348a8 --- /dev/null +++ b/config/helpers.php @@ -0,0 +1,16 @@ + Maize\Helpers\Helper::class, + +]; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..e69de29 diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..e005ac7 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,13 @@ +includes: + - phpstan-baseline.neon + +parameters: + level: 4 + paths: + - src + - config + tmpDir: build/phpstan + checkOctaneCompatibility: true + checkModelProperties: true + checkMissingIterableValueType: false + diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..290f954 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,39 @@ + + + + + tests + + + + + ./src + + + + + + + + + + + diff --git a/src/Helper.php b/src/Helper.php new file mode 100644 index 0000000..aa23d61 --- /dev/null +++ b/src/Helper.php @@ -0,0 +1,81 @@ +get('limit') ?? $default; + + return min($limit, $max); + } + + public static function anonymizeFilename(string $filename): string + { + if (empty($filename)) { + return $filename; + } + + $extension = pathinfo($filename, PATHINFO_EXTENSION); + $uuid = Str::uuid(); + + return "{$uuid}.{$extension}"; + } + + public static function sanitizeUrl(?string $url): ?string + { + if (empty($url)) { + return $url; + } + + $url = Str::lower($url); + + if (! Str::startsWith($url, ['http://', 'https://'])) { + return "https://{$url}"; + } + + return $url; + } + + public static function instanceofTypes(mixed $value, array | string | object $types): bool + { + foreach (Arr::wrap($types) as $type) { + if ($value instanceof $type) { + return true; + } + } + + return false; + } + + public static function classUsesTrait(string $trait, mixed $class): bool + { + return in_array( + $trait, + trait_uses_recursive($class) + ); + } + + public static function morphClassOf(Model | string $model): string + { + if (is_string($model)) { + $model = app($model); + } + + return $model->getMorphClass(); + } + + public static function modelKeyName(Model | string $model): string + { + if (is_string($model)) { + $model = app($model); + } + + return $model->getKeyName(); + } +} diff --git a/src/HelperServiceProvider.php b/src/HelperServiceProvider.php new file mode 100644 index 0000000..2a4e71f --- /dev/null +++ b/src/HelperServiceProvider.php @@ -0,0 +1,16 @@ +name('laravel-helpers') + ->hasConfigFile(); + } +} diff --git a/src/helpers.php b/src/helpers.php new file mode 100644 index 0000000..0827fdf --- /dev/null +++ b/src/helpers.php @@ -0,0 +1,12 @@ +merge([ + 'limit' => $limit, + ]); + } + + if (! is_null($max)) { + expect(hlp()->paginationLimit($default, $max))->toBe($result); + + return; + } + + if (! is_null($default)) { + expect(hlp()->paginationLimit($default))->toBe($result); + + return; + } + + expect(hlp()->paginationLimit())->toBe($result); +})->with([ + ['limit' => null, 'default' => null, 'max' => null, 'result' => 16], + ['limit' => 49, 'default' => null, 'max' => null, 'result' => 48], + ['limit' => null, 'default' => 8, 'max' => null, 'result' => 8], + ['limit' => 500, 'default' => 16, 'max' => 200, 'result' => 200], + +]); + +it('can anonymize filename', function (?string $name, ?string $ext) { + $filename = "{$name}{$ext}"; + $expect = expect(hlp()->anonymizeFilename($filename)); + + if (empty($filename)) { + $expect->toBeEmpty(); + + return; + } + + $expect->toEndWith($ext); + assertNotEquals($expect, $filename); +})->with([ + ['filename' => '', 'ext' => ''], + ['name' => 'test', 'ext' => '.zip'], + ['name' => 'test-test', 'ext' => '.jpg'], +]); + +it('can sanitize url', function (?string $url, $result) { + expect(hlp()->sanitizeUrl($url))->toBe($result); +})->with([ + ['url' => null, 'result' => null], + ['url' => 'https://test.test', 'result' => 'https://test.test'], + ['url' => 'http://test.test', 'result' => 'http://test.test'], + ['url' => 'HTTPS://TEST.TEST', 'result' => 'https://test.test'], + ['url' => 'HTTP://TEST.TEST', 'result' => 'http://test.test'], + ['url' => 'test.test', 'result' => 'https://test.test'], + ['url' => 'TEST.TEST', 'result' => 'https://test.test'], +]); + +it('is instance of types', function (mixed $value, mixed $types, bool $result) { + expect(hlp()->instanceofTypes($value, $types))->toBe($result); +})->with([ + ['value' => null, 'types' => [], 'result' => false], + ['value' => null, 'types' => [Model::class], 'result' => false], + ['value' => User::class, 'types' => [Model::class], 'result' => false], + ['value' => new User(), 'types' => [Model::class], 'result' => true], + ['value' => new User(), 'types' => [Model::class, 'test'], 'result' => true], + ['value' => new User(), 'types' => ['test'], 'result' => false], + ['value' => new class () {}, 'types' => ['test'], 'result' => false], + ['value' => new User(), 'types' => User::class, 'result' => true], + ['value' => new User(), 'types' => new User(), 'result' => true], + ['value' => new User(), 'types' => new class () {}, 'result' => false], + ['value' => new User(), 'types' => new Article(), 'result' => false], +]); + +it('class uses trait', function (string $trait, mixed $class, bool $result) { + expect(hlp()->classUsesTrait($trait, $class))->toBe($result); +})->with([ + ['trait' => HasFactory::class, 'class' => Article::class, 'result' => true], + ['trait' => HasFactory::class, 'class' => new Article(), 'result' => true], + ['trait' => Article::class, 'class' => HasFactory::class, 'result' => false], + ['trait' => Article::class, 'class' => User::class, 'result' => false], + ['trait' => HasFactory::class, 'class' => User::class, 'result' => false], +]); + +it('can get morph class of', function (mixed $model, string $result) { + expect(hlp()->morphClassOf($model))->toBe($result); +})->with([ + ['model' => Article::class, 'result' => Article::class], + ['model' => new Article(), 'result' => Article::class], +]); + +it('can get model key name', function (mixed $model, string $result) { + expect(hlp()->modelKeyName($model))->toBe($result); +})->with([ + ['model' => Article::class, 'result' => 'id'], + ['model' => new Article(), 'result' => 'id'], +]); diff --git a/tests/Models/Article.php b/tests/Models/Article.php new file mode 100644 index 0000000..a9cc67d --- /dev/null +++ b/tests/Models/Article.php @@ -0,0 +1,15 @@ +in(__DIR__); diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..38533d0 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,26 @@ +