├── .codeclimate.yml
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── coverage.yml
│ ├── php-cs-fixer.yml
│ ├── phpunit.yml
│ └── update-changelog.yml
├── .gitignore
├── .idea
├── .gitignore
├── blade.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── laravel-dumper.iml
├── laravel-idea-personal.xml
├── laravel-idea.xml
├── modules.xml
├── php-test-framework.xml
├── php.xml
├── phpunit.xml
└── vcs.xml
├── .php-cs-fixer.dist.php
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin
└── pre-commit.sh
├── composer.json
├── config.php
├── diffs
├── carbon-date.diff
├── container-nested.diff
├── container.diff
├── custom-caster-2.diff
├── custom-caster.diff
├── eloquent-builder.diff
├── eloquent-model.diff
├── eloquent-relation.diff
├── query-builder.diff
├── request.diff
└── response.diff
├── phpunit.xml
├── src
├── Casters
│ ├── BuilderCaster.php
│ ├── CarbonCaster.php
│ ├── Caster.php
│ ├── ContainerCaster.php
│ ├── CustomCaster.php
│ ├── DatabaseConnectionCaster.php
│ ├── HeaderBagCaster.php
│ ├── ModelCaster.php
│ ├── ParameterBagCaster.php
│ ├── RequestCaster.php
│ ├── ResponseCaster.php
│ └── manifest.php
├── LaravelDumperServiceProvider.php
├── Support
│ ├── Key.php
│ └── Properties.php
├── autoload.php
└── helpers.php
└── tests
├── CasterTest.php
├── CustomCasterTest.php
├── DatabaseCasterTest.php
├── PropertiesTest.php
├── TestCase.php
└── migrations
└── 2022_01_18_131057_create_test_tables.php
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | exclude_patterns:
2 | - ".github/"
3 | - ".idea/"
4 | - "stubs/"
5 | - "tests/"
6 | - "**/vendor/"
7 | - "**/node_modules/"
8 | - "bin/"
9 | - "diffs/"
10 | - "*.md"
11 | - ".*.yml"
12 | - "LICENSE"
13 | - "composer.json"
14 | - "phpunit.xml"
15 | - "src/helpers.php"
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **What version does this affect?**
14 | - Laravel Version: [e.g. 5.8.0]
15 | - Package Version: [e.g. 1.5.0]
16 |
17 | **To Reproduce**
18 | Steps to reproduce the behavior:
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Additional context**
24 | Add any other context about the problem here.
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest a new feature idea or improvement
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | name: Code Coverage
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | coverage:
10 | runs-on: ubuntu-latest
11 |
12 | name: Publish code coverage
13 |
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v3
17 |
18 | - name: Setup PHP
19 | uses: shivammathur/setup-php@v2
20 | with:
21 | php-version: 8.2
22 | extensions: dom, curl, libxml, mbstring, zip, pcntl, bcmath, intl, iconv
23 | coverage: pcov
24 |
25 | - name: Cache dependencies
26 | uses: actions/cache@v2
27 | with:
28 | path: |
29 | vendor
30 | ${{ steps.composer-cache-files-dir.outputs.dir }}
31 | key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}
32 | restore-keys: |
33 | ${{ runner.os }}-composer-
34 |
35 | - name: Install dependencies
36 | env:
37 | COMPOSER_DISCARD_CHANGES: true
38 | run: composer require --no-suggest --no-progress --no-interaction --prefer-dist --update-with-all-dependencies "laravel/framework:^10.0" "orchestra/testbench:^8.0"
39 |
40 | - name: Run and publish code coverage
41 | uses: paambaati/codeclimate-action@v2.4.0
42 | env:
43 | CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
44 | with:
45 | coverageCommand: vendor/bin/phpunit --coverage-clover ${{ github.workspace }}/clover.xml
46 | debug: true
47 | coverageLocations:
48 | "${{github.workspace}}/clover.xml:clover"
49 |
--------------------------------------------------------------------------------
/.github/workflows/php-cs-fixer.yml:
--------------------------------------------------------------------------------
1 | name: Code Style
2 |
3 | on: [ pull_request, push ]
4 |
5 | jobs:
6 | coverage:
7 | runs-on: ubuntu-latest
8 |
9 | name: Run code style checks
10 |
11 | steps:
12 | - name: Checkout code
13 | uses: actions/checkout@v3
14 |
15 | - name: Setup PHP
16 | uses: shivammathur/setup-php@v2
17 | with:
18 | php-version: 8.1
19 | extensions: dom, curl, libxml, mbstring, zip, pcntl, bcmath, intl, iconv
20 |
21 | - name: Cache dependencies
22 | uses: actions/cache@v2
23 | with:
24 | path: |
25 | vendor
26 | ${{ steps.composer-cache-files-dir.outputs.dir }}
27 | key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}
28 | restore-keys: |
29 | ${{ runner.os }}-composer-
30 |
31 | - name: Install dependencies
32 | env:
33 | COMPOSER_DISCARD_CHANGES: true
34 | run: composer require --no-suggest --no-progress --no-interaction --prefer-dist --update-with-all-dependencies "laravel/framework:^10.0" "orchestra/testbench:^8.0"
35 |
36 | - name: Run PHP CS Fixer
37 | run: ./vendor/bin/php-cs-fixer fix --diff --dry-run
38 |
--------------------------------------------------------------------------------
/.github/workflows/phpunit.yml:
--------------------------------------------------------------------------------
1 | name: PHPUnit
2 |
3 | on:
4 | push:
5 | pull_request:
6 | schedule:
7 | - cron: '0 14 * * 3' # Run Wednesdays at 2pm EST
8 |
9 | jobs:
10 | php-tests:
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | matrix:
15 | dependency-version: [ stable, lowest ]
16 | laravel: [ ^9.50.2, 10.* ]
17 | php: [ 8.1, 8.2 ]
18 | include:
19 | - laravel: ^9.50.2
20 | testbench: ^7.22
21 | - laravel: 10.*
22 | testbench: 8.*
23 |
24 | timeout-minutes: 10
25 | name: "${{ matrix.php }} / ${{ matrix.laravel }} (${{ matrix.dependency-version }})"
26 |
27 | steps:
28 | - name: Checkout code
29 | uses: actions/checkout@v3
30 |
31 | - name: Setup PHP
32 | uses: shivammathur/setup-php@v2
33 | with:
34 | php-version: ${{ matrix.php }}
35 | extensions: dom, curl, libxml, mbstring, zip, pcntl, bcmath, intl, iconv
36 | tools: composer:v2
37 |
38 | - name: Register composer cache directory
39 | id: composer-cache-files-dir
40 | run: |
41 | echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
42 |
43 | - name: Cache dependencies
44 | uses: actions/cache@v3
45 | with:
46 | path: |
47 | vendor
48 | ${{ steps.composer-cache-files-dir.outputs.dir }}
49 | key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}
50 | restore-keys: |
51 | ${{ runner.os }}-composer-
52 |
53 | - name: Install dependencies
54 | run: composer require --no-interaction --prefer-dist --no-update "illuminate/support:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}"
55 |
56 | - name: Set dependency version
57 | run: composer update --no-interaction --prefer-dist --with-all-dependencies --prefer-${{ matrix.dependency-version }}
58 |
59 | - name: Execute tests
60 | run: vendor/bin/phpunit -v
61 |
--------------------------------------------------------------------------------
/.github/workflows/update-changelog.yml:
--------------------------------------------------------------------------------
1 | name: Update Changelog
2 |
3 | on:
4 | release:
5 | types: [ published ]
6 |
7 | jobs:
8 | update-publish:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v3
12 | with:
13 | repository: ${{ github.event.repository.full_name }}
14 | ref: 'main'
15 |
16 | - name: Update changelog
17 | uses: thomaseizinger/keep-a-changelog-new-release@v1
18 | with:
19 | version: ${{ github.event.release.tag_name }}
20 |
21 | - name: Commit changelog back to repo
22 | uses: EndBug/add-and-commit@v8
23 | with:
24 | add: 'CHANGELOG.md'
25 | message: ${{ github.event.release.tag_name }}
26 | env:
27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | composer.phar
3 | composer.lock
4 | .phpunit.result.cache
5 | .php-cs-fixer.cache
6 |
7 | .DS_Store
8 | .phpstorm.meta.php
9 | _ide_helper.php
10 |
11 | node_modules
12 | mix-manifest.json
13 | yarn-error.log
14 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/blade.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/.idea/laravel-dumper.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/.idea/laravel-idea-personal.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/laravel-idea.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/php-test-framework.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/php.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
--------------------------------------------------------------------------------
/.idea/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.php-cs-fixer.dist.php:
--------------------------------------------------------------------------------
1 | setRiskyAllowed(true)
7 | ->setIndent("\t")
8 | ->setLineEnding("\n")
9 | ->setRules([
10 | '@PSR2' => true,
11 | 'function_declaration' => [
12 | 'closure_function_spacing' => 'none',
13 | 'closure_fn_spacing' => 'none',
14 | ],
15 | 'ordered_imports' => [
16 | 'sort_algorithm' => 'alpha',
17 | ],
18 | 'array_indentation' => true,
19 | 'braces' => [
20 | 'allow_single_line_closure' => true,
21 | ],
22 | 'no_break_comment' => false,
23 | 'return_type_declaration' => [
24 | 'space_before' => 'none',
25 | ],
26 | 'blank_line_after_opening_tag' => true,
27 | 'compact_nullable_typehint' => true,
28 | 'cast_spaces' => true,
29 | 'concat_space' => [
30 | 'spacing' => 'none',
31 | ],
32 | 'declare_equal_normalize' => [
33 | 'space' => 'none',
34 | ],
35 | 'function_typehint_space' => true,
36 | 'new_with_braces' => true,
37 | 'method_argument_space' => true,
38 | 'no_empty_statement' => true,
39 | 'no_empty_comment' => true,
40 | 'no_empty_phpdoc' => true,
41 | 'no_extra_blank_lines' => [
42 | 'tokens' => [
43 | 'extra',
44 | 'use',
45 | 'use_trait',
46 | 'return',
47 | ],
48 | ],
49 | 'no_leading_import_slash' => true,
50 | 'no_leading_namespace_whitespace' => true,
51 | 'no_blank_lines_after_class_opening' => true,
52 | 'no_blank_lines_after_phpdoc' => true,
53 | 'no_whitespace_in_blank_line' => false,
54 | 'no_whitespace_before_comma_in_array' => true,
55 | 'no_useless_else' => true,
56 | 'no_useless_return' => true,
57 | 'single_trait_insert_per_statement' => true,
58 | 'psr_autoloading' => true,
59 | 'dir_constant' => true,
60 | 'single_line_comment_style' => [
61 | 'comment_types' => ['hash'],
62 | ],
63 | 'include' => true,
64 | 'is_null' => true,
65 | 'linebreak_after_opening_tag' => true,
66 | 'lowercase_cast' => true,
67 | 'lowercase_static_reference' => true,
68 | 'magic_constant_casing' => true,
69 | 'magic_method_casing' => true,
70 | 'class_attributes_separation' => [
71 | // TODO: This can be reverted when https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/5869 is merged
72 | 'elements' => ['const' => 'one', 'method' => 'one', 'property' => 'one'],
73 | ],
74 | 'modernize_types_casting' => true,
75 | 'native_function_casing' => true,
76 | 'native_function_type_declaration_casing' => true,
77 | 'no_alias_functions' => true,
78 | 'no_multiline_whitespace_around_double_arrow' => true,
79 | 'multiline_whitespace_before_semicolons' => true,
80 | 'no_short_bool_cast' => true,
81 | 'no_unused_imports' => true,
82 | 'no_php4_constructor' => true,
83 | 'no_singleline_whitespace_before_semicolons' => true,
84 | 'no_spaces_around_offset' => true,
85 | 'no_trailing_comma_in_list_call' => true,
86 | 'no_trailing_comma_in_singleline_array' => true,
87 | 'normalize_index_brace' => true,
88 | 'object_operator_without_whitespace' => true,
89 | 'phpdoc_annotation_without_dot' => true,
90 | 'phpdoc_indent' => true,
91 | 'phpdoc_no_package' => true,
92 | 'phpdoc_no_access' => true,
93 | 'phpdoc_no_useless_inheritdoc' => true,
94 | 'phpdoc_single_line_var_spacing' => true,
95 | 'phpdoc_trim' => true,
96 | 'phpdoc_types' => true,
97 | 'semicolon_after_instruction' => true,
98 | 'array_syntax' => [
99 | 'syntax' => 'short',
100 | ],
101 | 'list_syntax' => [
102 | 'syntax' => 'short',
103 | ],
104 | 'short_scalar_cast' => true,
105 | 'single_blank_line_before_namespace' => true,
106 | 'single_quote' => true,
107 | 'standardize_not_equals' => true,
108 | 'ternary_operator_spaces' => true,
109 | 'whitespace_after_comma_in_array' => true,
110 | 'not_operator_with_successor_space' => true,
111 | 'trailing_comma_in_multiline' => true,
112 | 'trim_array_spaces' => true,
113 | 'binary_operator_spaces' => true,
114 | 'unary_operator_spaces' => true,
115 | 'php_unit_method_casing' => [
116 | 'case' => 'snake_case',
117 | ],
118 | 'php_unit_test_annotation' => [
119 | 'style' => 'prefix',
120 | ],
121 | ])
122 | ->setFinder(
123 | PhpCsFixer\Finder::create()
124 | ->exclude('.circleci')
125 | ->exclude('bin')
126 | ->exclude('node_modules')
127 | ->exclude('vendor')
128 | ->notPath('.phpstorm.meta.php')
129 | ->notPath('_ide_helper.php')
130 | ->notPath('artisan')
131 | ->in(__DIR__)
132 | );
133 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes will be documented in this file following the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
4 | format. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5 |
6 | ## [Unreleased]
7 |
8 | ## [2.0.0] - 2023-12-16
9 |
10 | ## [1.1.0] - 2023-02-17
11 |
12 | ### Added
13 |
14 | - Added Laravel 10 support
15 |
16 | ## [1.0.1] - 2022-02-10
17 |
18 | ### Fixed
19 |
20 | - Fixed how bindings are merged into SQL queries when dumping a query builder
21 |
22 | ## [1.0.0] - 2022-01-24
23 |
24 | ## [0.5.0] - 2022-01-23
25 |
26 | ### Changed
27 |
28 | - Removed the `LaravelDumper` facade in favor of registering custom casters directly
29 |
30 | ## [0.4.0]
31 |
32 | ### Added
33 |
34 | - Added Laravel 9 support
35 |
36 | ## [0.3.0]
37 |
38 | ### Added
39 |
40 | - Added support for dynamic custom casters
41 | - Added `LaravelDumper` facade
42 | - Added custom `Properties` collection for easier manipulation of dumped properties
43 |
44 | ### Changed
45 |
46 | - Changed `Caster` interface to use `Properties` collection
47 | - Updated all casters to use new `Properties` collection
48 |
49 | ## [0.2.0]
50 |
51 | ### Added
52 |
53 | - Added `ddf()` and `dumpf()` for access to original `dd()` and `dump()` behavior
54 |
55 | ## [0.1.0]
56 |
57 | ### Added
58 |
59 | - Added support for Requests and Responses
60 | - Added support for ParameterBags
61 | - Added support for HeaderBags
62 |
63 | ### Changed
64 |
65 | - Improved tests
66 |
67 | ## [0.0.1]
68 |
69 | ### Added
70 |
71 | - Initial release
72 |
73 | # Keep a Changelog Syntax
74 |
75 | - `Added` for new features.
76 | - `Changed` for changes in existing functionality.
77 | - `Deprecated` for soon-to-be removed features.
78 | - `Removed` for now removed features.
79 | - `Fixed` for any bug fixes.
80 | - `Security` in case of vulnerabilities.
81 |
82 | [Unreleased]: https://github.com/glhd/laravel-dumper/compare/2.0.0...HEAD
83 |
84 | [2.0.0]: https://github.com/glhd/laravel-dumper/compare/1.1.0...2.0.0
85 |
86 | [1.1.0]: https://github.com/glhd/laravel-dumper/compare/1.0.1...1.1.0
87 |
88 | [1.0.1]: https://github.com/glhd/laravel-dumper/compare/1.0.0...1.0.1
89 |
90 | [1.0.0]: https://github.com/glhd/laravel-dumper/compare/0.5.0...1.0.0
91 |
92 | [0.5.0]: https://github.com/glhd/laravel-dumper/compare/0.4.0...0.5.0
93 |
94 | [0.4.0]: https://github.com/glhd/laravel-dumper/compare/0.3.0...0.4.0
95 |
96 | [0.3.0]: https://github.com/glhd/laravel-dumper/compare/0.2.0...0.3.0
97 |
98 | [0.2.0]: https://github.com/glhd/laravel-dumper/compare/0.1.0...0.2.0
99 |
100 | [0.1.0]: https://github.com/glhd/laravel-dumper/compare/0.0.1...0.1.0
101 |
102 | [0.0.1]: https://github.com/glhd/laravel-dumper
103 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Galahad
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
33 |
34 | # Laravel Dumper
35 |
36 | Improve the default output of `dump()` and `dd()` in Laravel projects. Improves the default
37 | dump behavior for many core Laravel objects, including:
38 |
39 | - Models
40 | - Query Builders
41 | - Service Container
42 | - Database Connections
43 | - Carbon Instances
44 | - Requests and Responses
45 |
46 | https://user-images.githubusercontent.com/21592/150163719-547ecd90-b029-4588-9648-34891e5e0886.mp4
47 |
48 | ## Installation
49 |
50 | Install as a dev dependency:
51 |
52 | ```shell
53 | # composer require glhd/laravel-dumper --dev
54 | ```
55 |
56 | ## Usage
57 |
58 | Just use `dd()` as you would normally, and enjoy the newly curated output!
59 |
60 | ## Original Dump Output
61 | If, for some reason, you really need the full debug output for an object that `laravel-dumper` customizes, you can
62 | do a "full" dump with `ddf()` and `dumpf()`.
63 |
64 | ## Comparison to Default Output
65 |
66 | You can see comparisons between the default `dd()` output and the `laravel-dumper` output
67 | in the [diffs directory of this repository](./diffs/).
68 |
69 | ## Custom Casters
70 |
71 | Due to [changes in how Laravel registers the var dumper](https://github.com/laravel/framework/pull/44211) it
72 | is no longer possible to register custom casters.
73 |
--------------------------------------------------------------------------------
/bin/pre-commit.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Move into project root
4 | BIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5 | cd "$BIN_DIR"
6 | cd ..
7 |
8 | # Exit on errors
9 | set -e
10 |
11 | CHANGED_FILES=$(git diff --cached --name-only --diff-filter=ACM -- '***.php')
12 |
13 | if [[ -z "$CHANGED_FILES" ]]; then
14 | echo 'No changed files'
15 | exit 0
16 | fi
17 |
18 | if [[ -x vendor/bin/php-cs-fixer ]]; then
19 | vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php $CHANGED_FILES
20 | git add $CHANGED_FILES
21 | else
22 | echo 'PHP-CS-Fixer is not installed'
23 | exit 1
24 | fi
25 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "glhd/laravel-dumper",
3 | "description": "",
4 | "keywords": [
5 | "laravel"
6 | ],
7 | "authors": [
8 | {
9 | "name": "Chris Morrell",
10 | "homepage": "http://www.cmorrell.com"
11 | }
12 | ],
13 | "type": "library",
14 | "license": "MIT",
15 | "require": {
16 | "php": ">=8.1",
17 | "illuminate/support": "^9|^10|11.x-dev|dev-master",
18 | "ext-json": "*",
19 | "jdorn/sql-formatter": "^1.2"
20 | },
21 | "require-dev": {
22 | "orchestra/testbench": "^7.10|^8|9.x-dev|10.x-dev|dev-master",
23 | "friendsofphp/php-cs-fixer": "^3.0",
24 | "mockery/mockery": "^1.3.2",
25 | "phpunit/phpunit": "^9.5"
26 | },
27 | "autoload": {
28 | "psr-4": {
29 | "Glhd\\LaravelDumper\\": "src/"
30 | },
31 | "files": [
32 | "src/autoload.php"
33 | ]
34 | },
35 | "autoload-dev": {
36 | "classmap": [
37 | "tests/TestCase.php"
38 | ],
39 | "psr-4": {
40 | "Glhd\\LaravelDumper\\Tests\\": "tests/"
41 | }
42 | },
43 | "scripts": {
44 | "write-diffs": "WRITE_DIFFS=1 vendor/bin/phpunit",
45 | "fix-style": "vendor/bin/php-cs-fixer fix",
46 | "check-style": "vendor/bin/php-cs-fixer fix --diff --dry-run"
47 | },
48 | "extra": {
49 | "laravel": {
50 | "providers": [
51 | "Glhd\\LaravelDumper\\LaravelDumperServiceProvider"
52 | ]
53 | }
54 | },
55 | "minimum-stability": "dev",
56 | "prefer-stable": true
57 | }
58 |
--------------------------------------------------------------------------------
/config.php:
--------------------------------------------------------------------------------
1 | ['local', 'testing'],
16 | ];
17 |
--------------------------------------------------------------------------------
/diffs/carbon-date.diff:
--------------------------------------------------------------------------------
1 | --- Before 2022-01-24 09:44:04.000000000 -0500
2 | +++ After 2022-01-24 09:44:04.000000000 -0500
3 | @@ -1,23 +1,12 @@
4 | Carbon\Carbon @1642553042 {
5 | + date: 2022-01-18 19:44:02.572622 America/New_York (-05:00)
6 | #endOfTime: false
7 | #startOfTime: false
8 | #constructedObjectId: "00000000186315cd0000000058605208"
9 | - #localMonthsOverflow: null
10 | - #localYearsOverflow: null
11 | - #localStrictModeEnabled: null
12 | - #localHumanDiffOptions: null
13 | - #localToStringFormat: null
14 | - #localSerializer: null
15 | - #localMacros: null
16 | - #localGenericMacros: null
17 | - #localFormatFunction: null
18 | - #localTranslator: null
19 | #dumpProperties: array:3 [
20 | 0 => "date"
21 | 1 => "timezone_type"
22 | 2 => "timezone"
23 | ]
24 | - #dumpLocale: null
25 | - #dumpDateProperties: null
26 | - date: 2022-01-18 19:44:02.572622 America/New_York (-05:00)
27 | + …12
28 | }
29 |
--------------------------------------------------------------------------------
/diffs/container-nested.diff:
--------------------------------------------------------------------------------
1 | --- Before 2022-01-24 09:44:04.000000000 -0500
2 | +++ After 2022-01-24 09:44:04.000000000 -0500
3 | @@ -1,23 +1,3 @@
4 | array:1 [
5 | - 0 => Illuminate\Container\Container {
6 | - #resolved: []
7 | - #bindings: []
8 | - #methodBindings: []
9 | - #instances: []
10 | - #scopedInstances: []
11 | - #aliases: []
12 | - #abstractAliases: []
13 | - #extenders: []
14 | - #tags: []
15 | - #buildStack: []
16 | - #with: []
17 | - +contextual: []
18 | - #reboundCallbacks: []
19 | - #globalBeforeResolvingCallbacks: []
20 | - #globalResolvingCallbacks: []
21 | - #globalAfterResolvingCallbacks: []
22 | - #beforeResolvingCallbacks: []
23 | - #resolvingCallbacks: []
24 | - #afterResolvingCallbacks: []
25 | - }
26 | + 0 => Illuminate\Container\Container { …19}
27 | ]
28 |
--------------------------------------------------------------------------------
/diffs/container.diff:
--------------------------------------------------------------------------------
1 | --- Before 2022-01-24 09:44:04.000000000 -0500
2 | +++ After 2022-01-24 09:44:04.000000000 -0500
3 | @@ -1,7 +1,4 @@
4 | Illuminate\Container\Container {
5 | - #resolved: array:1 [
6 | - "Glhd\LaravelDumper\Tests\CasterTest" => true
7 | - ]
8 | #bindings: array:1 [
9 | "Glhd\LaravelDumper\Tests\CasterTest" => array:2 [
10 | "concrete" => Closure() {
11 | @@ -13,16 +10,11 @@
12 | "shared" => false
13 | ]
14 | ]
15 | - #methodBindings: []
16 | - #instances: []
17 | - #scopedInstances: []
18 | #aliases: array:1 [
19 | "bar" => "Glhd\LaravelDumper\Tests\CasterTest"
20 | ]
21 | - #abstractAliases: array:1 [
22 | - "Glhd\LaravelDumper\Tests\CasterTest" => array:1 [
23 | - 0 => "bar"
24 | - ]
25 | + #resolved: array:1 [
26 | + "Glhd\LaravelDumper\Tests\CasterTest" => true
27 | ]
28 | #extenders: array:1 [
29 | "Glhd\LaravelDumper\Tests\CasterTest" => array:1 [
30 | @@ -34,15 +26,5 @@
31 | }
32 | ]
33 | ]
34 | - #tags: []
35 | - #buildStack: []
36 | - #with: []
37 | - +contextual: []
38 | - #reboundCallbacks: []
39 | - #globalBeforeResolvingCallbacks: []
40 | - #globalResolvingCallbacks: []
41 | - #globalAfterResolvingCallbacks: []
42 | - #beforeResolvingCallbacks: []
43 | - #resolvingCallbacks: []
44 | - #afterResolvingCallbacks: []
45 | + …15
46 | }
47 |
--------------------------------------------------------------------------------
/diffs/custom-caster-2.diff:
--------------------------------------------------------------------------------
1 | --- Before 2022-01-24 09:44:05.000000000 -0500
2 | +++ After 2022-01-24 09:44:05.000000000 -0500
3 | @@ -1,3 +1,3 @@
4 | Glhd\LaravelDumper\Tests\MyOtherCustomObject {
5 | - #bar: "bar"
6 | + foo: "bar"
7 | }
8 |
--------------------------------------------------------------------------------
/diffs/custom-caster.diff:
--------------------------------------------------------------------------------
1 | --- Before 2022-01-24 09:44:05.000000000 -0500
2 | +++ After 2022-01-24 09:44:05.000000000 -0500
3 | @@ -1,6 +1,6 @@
4 | Glhd\LaravelDumper\Tests\MyCustomObject {
5 | + +"dyn": "this is a dynamic prop"
6 | #foo: "foo"
7 | - #bar: "bar"
8 | - #nothing: null
9 | - #nah: []
10 | + virt: "this is a virtual prop"
11 | + …1
12 | }
13 |
--------------------------------------------------------------------------------
/diffs/eloquent-model.diff:
--------------------------------------------------------------------------------
1 | --- Before 2022-01-24 09:44:05.000000000 -0500
2 | +++ After 2022-01-24 09:44:05.000000000 -0500
3 | @@ -1,25 +1,28 @@
4 | Glhd\LaravelDumper\Tests\User {
5 | - #guarded: []
6 | - #connection: "testing"
7 | - #table: "users"
8 | - #primaryKey: "id"
9 | - #keyType: "int"
10 | - +incrementing: true
11 | - #with: []
12 | - #withCount: []
13 | - +preventsLazyLoading: false
14 | - #perPage: 15
15 | + +"id": 1
16 | + +"company_id": 1
17 | + +"email": "foo@bar.com"
18 | + +"name": "Chris"
19 | + +"created_at": "2022-01-24 09:44:05"
20 | + +"updated_at": "2022-01-24 09:44:05"
21 | + isDirty(): true
22 | +exists: true
23 | +wasRecentlyCreated: true
24 | - #escapeWhenCastingToString: false
25 | - #attributes: array:6 [
26 | - "name" => "Chris"
27 | - "email" => "foo@bar.com"
28 | - "company_id" => 1
29 | - "updated_at" => "2022-01-24 09:44:05"
30 | - "created_at" => "2022-01-24 09:44:05"
31 | - "id" => 1
32 | + #relations: array:1 [
33 | + "company" => Glhd\LaravelDumper\Tests\Company {
34 | + +"id": 1
35 | + +"name": "Galahad"
36 | + +"created_at": "2022-01-24 09:44:05"
37 | + +"updated_at": "2022-01-24 09:44:05"
38 | + isDirty(): false
39 | + +exists: true
40 | + +wasRecentlyCreated: true
41 | + #relations: []
42 | + …27
43 | + }
44 | ]
45 | + #connection: "testing"
46 | + #table: "users"
47 | #original: array:6 [
48 | "name" => "John"
49 | "email" => "foo@bar.com"
50 | @@ -29,61 +32,5 @@
51 | "id" => 1
52 | ]
53 | #changes: []
54 | - #casts: []
55 | - #classCastCache: []
56 | - #attributeCastCache: []
57 | - #dates: []
58 | - #dateFormat: null
59 | - #appends: []
60 | - #dispatchesEvents: []
61 | - #observables: []
62 | - #relations: array:1 [
63 | - "company" => Glhd\LaravelDumper\Tests\Company {
64 | - #guarded: []
65 | - #connection: "testing"
66 | - #table: "companies"
67 | - #primaryKey: "id"
68 | - #keyType: "int"
69 | - +incrementing: true
70 | - #with: []
71 | - #withCount: []
72 | - +preventsLazyLoading: false
73 | - #perPage: 15
74 | - +exists: true
75 | - +wasRecentlyCreated: true
76 | - #escapeWhenCastingToString: false
77 | - #attributes: array:4 [
78 | - "name" => "Galahad"
79 | - "updated_at" => "2022-01-24 09:44:05"
80 | - "created_at" => "2022-01-24 09:44:05"
81 | - "id" => 1
82 | - ]
83 | - #original: array:4 [
84 | - "name" => "Galahad"
85 | - "updated_at" => "2022-01-24 09:44:05"
86 | - "created_at" => "2022-01-24 09:44:05"
87 | - "id" => 1
88 | - ]
89 | - #changes: []
90 | - #casts: []
91 | - #classCastCache: []
92 | - #attributeCastCache: []
93 | - #dates: []
94 | - #dateFormat: null
95 | - #appends: []
96 | - #dispatchesEvents: []
97 | - #observables: []
98 | - #relations: []
99 | - #touches: []
100 | - +timestamps: true
101 | - #hidden: []
102 | - #visible: []
103 | - #fillable: []
104 | - }
105 | - ]
106 | - #touches: []
107 | - +timestamps: true
108 | - #hidden: []
109 | - #visible: []
110 | - #fillable: []
111 | + …23
112 | }
113 |
--------------------------------------------------------------------------------
/diffs/request.diff:
--------------------------------------------------------------------------------
1 | --- Before 2022-01-24 09:44:04.000000000 -0500
2 | +++ After 2022-01-24 09:44:04.000000000 -0500
3 | @@ -1,86 +1,44 @@
4 | Illuminate\Http\Request {
5 | - #json: null
6 | - #convertedFiles: null
7 | - #userResolver: null
8 | - #routeResolver: null
9 | - +attributes: Symfony\Component\HttpFoundation\ParameterBag {
10 | - #parameters: []
11 | - }
12 | - +request: Symfony\Component\HttpFoundation\InputBag {
13 | - #parameters: []
14 | - }
15 | - +query: Symfony\Component\HttpFoundation\InputBag {
16 | - #parameters: []
17 | - }
18 | + +attributes: Symfony\Component\HttpFoundation\ParameterBag {}
19 | + +request: Symfony\Component\HttpFoundation\InputBag {}
20 | + +query: Symfony\Component\HttpFoundation\InputBag {}
21 | +server: Symfony\Component\HttpFoundation\ServerBag {
22 | - #parameters: array:17 [
23 | - "SERVER_NAME" => "localhost"
24 | - "SERVER_PORT" => 80
25 | - "HTTP_HOST" => "localhost"
26 | - "HTTP_USER_AGENT" => "Symfony"
27 | - "HTTP_ACCEPT" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
28 | - "HTTP_ACCEPT_LANGUAGE" => "en-us,en;q=0.5"
29 | - "HTTP_ACCEPT_CHARSET" => "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
30 | - "REMOTE_ADDR" => "127.0.0.1"
31 | - "SCRIPT_NAME" => ""
32 | - "SCRIPT_FILENAME" => ""
33 | - "SERVER_PROTOCOL" => "HTTP/1.1"
34 | - "REQUEST_TIME" => 1643035444
35 | - "REQUEST_TIME_FLOAT" => 1643035444.9632
36 | - "PATH_INFO" => ""
37 | - "REQUEST_METHOD" => "GET"
38 | - "REQUEST_URI" => "/1"
39 | - "QUERY_STRING" => ""
40 | - ]
41 | - }
42 | - +files: Symfony\Component\HttpFoundation\FileBag {
43 | - #parameters: []
44 | - }
45 | - +cookies: Symfony\Component\HttpFoundation\InputBag {
46 | - #parameters: []
47 | + SERVER_NAME: "localhost"
48 | + SERVER_PORT: 80
49 | + HTTP_HOST: "localhost"
50 | + HTTP_USER_AGENT: "Symfony"
51 | + HTTP_ACCEPT: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
52 | + HTTP_ACCEPT_LANGUAGE: "en-us,en;q=0.5"
53 | + HTTP_ACCEPT_CHARSET: "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
54 | + REMOTE_ADDR: "127.0.0.1"
55 | + SCRIPT_NAME: ""
56 | + SCRIPT_FILENAME: ""
57 | + SERVER_PROTOCOL: "HTTP/1.1"
58 | + REQUEST_TIME: 1643035444
59 | + REQUEST_TIME_FLOAT: 1643035444.9632
60 | + PATH_INFO: ""
61 | + REQUEST_METHOD: "GET"
62 | + REQUEST_URI: "/1"
63 | + QUERY_STRING: ""
64 | }
65 | + +files: Symfony\Component\HttpFoundation\FileBag {}
66 | + +cookies: Symfony\Component\HttpFoundation\InputBag {}
67 | +headers: Symfony\Component\HttpFoundation\HeaderBag {
68 | - #headers: array:5 [
69 | - "host" => array:1 [
70 | - 0 => "localhost"
71 | - ]
72 | - "user-agent" => array:1 [
73 | - 0 => "Symfony"
74 | - ]
75 | - "accept" => array:1 [
76 | - 0 => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
77 | - ]
78 | - "accept-language" => array:1 [
79 | - 0 => "en-us,en;q=0.5"
80 | - ]
81 | - "accept-charset" => array:1 [
82 | - 0 => "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
83 | - ]
84 | - ]
85 | + host: "localhost"
86 | + user-agent: "Symfony"
87 | + accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
88 | + accept-language: "en-us,en;q=0.5"
89 | + accept-charset: "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
90 | #cacheControl: []
91 | }
92 | - #content: null
93 | - #languages: null
94 | - #charsets: null
95 | - #encodings: null
96 | - #acceptableContentTypes: null
97 | - #pathInfo: null
98 | - #requestUri: null
99 | - #baseUrl: null
100 | - #basePath: null
101 | - #method: null
102 | - #format: null
103 | - #session: null
104 | - #locale: null
105 | #defaultLocale: "en"
106 | - -preferredFormat: null
107 | -isHostValid: true
108 | -isForwardedValid: true
109 | - -isSafeContentPreferred: null
110 | pathInfo: "/1"
111 | requestUri: "/1"
112 | baseUrl: ""
113 | basePath: ""
114 | method: "GET"
115 | format: "html"
116 | + …19
117 | }
118 |
--------------------------------------------------------------------------------
/diffs/response.diff:
--------------------------------------------------------------------------------
1 | --- Before 2022-01-24 09:44:04.000000000 -0500
2 | +++ After 2022-01-24 09:44:04.000000000 -0500
3 | @@ -1,29 +1,13 @@
4 | Illuminate\Http\Response {
5 | +headers: Symfony\Component\HttpFoundation\ResponseHeaderBag {
6 | - #computedCacheControl: array:2 [
7 | - "no-cache" => true
8 | - "private" => true
9 | - ]
10 | - #cookies: []
11 | - #headerNames: array:2 [
12 | - "cache-control" => "Cache-Control"
13 | - "date" => "Date"
14 | - ]
15 | - #headers: array:2 [
16 | - "cache-control" => array:1 [
17 | - 0 => "no-cache, private"
18 | - ]
19 | - "date" => array:1 [
20 | - 0 => "Mon, 24 Jan 2022 14:44:04 GMT"
21 | - ]
22 | - ]
23 | + cache-control: "no-cache, private"
24 | + date: "Mon, 24 Jan 2022 14:44:04 GMT"
25 | #cacheControl: []
26 | }
27 | #content: "Hello world."
28 | #version: "1.0"
29 | #statusCode: 200
30 | #statusText: "OK"
31 | - #charset: null
32 | +original: "Hello world."
33 | - +exception: null
34 | + …2
35 | }
36 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 | ./src
11 |
12 |
13 |
14 |
15 | ./tests
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/Casters/BuilderCaster.php:
--------------------------------------------------------------------------------
1 | putVirtual('sql', $this->formatSql($target));
36 | $result->putProtected('connection', $target->getConnection());
37 |
38 | if ($target instanceof EloquentBuilder) {
39 | $result->copyAndCutProtected('model', $properties);
40 | $result->copyProtected('eagerLoad', $properties);
41 | }
42 |
43 | if ($target instanceof Relation) {
44 | $result->copyAndCutProtected('parent', $properties);
45 | $result->copyAndCutProtected('related', $properties);
46 | }
47 |
48 | $result->applyCutsToStub($stub, $properties);
49 |
50 | return $result->all();
51 | }
52 |
53 | protected function formatSql($target): string
54 | {
55 | try {
56 | if (method_exists($target, 'toRawSql')) {
57 | return $target->toRawSql();
58 | }
59 | } catch (Throwable $e) {
60 | // Just fall back on naive formatter below
61 | }
62 |
63 | $sql = $target->toSql();
64 | $bindings = Arr::flatten($target->getBindings());
65 | $merged = preg_replace_callback('/\?/', function() use (&$bindings) {
66 | return DB::getPdo()->quote(array_shift($bindings));
67 | }, $sql);
68 |
69 | if (strlen($merged) > 120) {
70 | $merged = SqlFormatter::format($merged, false);
71 | }
72 |
73 | return $merged;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Casters/CarbonCaster.php:
--------------------------------------------------------------------------------
1 | putVirtual('date', $target->format($this->getFormat($target)))
25 | ->when($is_nested, fn(Properties $properties) => $properties->only('date'))
26 | ->filter()
27 | ->reorder(['date', '*'])
28 | ->applyCutsToStub($stub, $properties)
29 | ->all();
30 | }
31 |
32 | protected function getFormat(CarbonInterface $target): string
33 | {
34 | // Only include microseconds if we have it
35 | $microseconds = '000000' === $target->format('u')
36 | ? ''
37 | : '.u';
38 |
39 | // Only include timezone name ("America/New_York") if we have it
40 | $timezone = $target->getTimezone()->getLocation()
41 | ? ' e (P)'
42 | : ' P';
43 |
44 | return 'Y-m-d H:i:s'.$microseconds.$timezone;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Casters/Caster.php:
--------------------------------------------------------------------------------
1 | singleton(static::class);
27 | }
28 |
29 | public static function callback($caster): Closure
30 | {
31 | return static function($target, array $properties, Stub $stub, bool $is_nested, int $filter = 0) use ($caster) {
32 | $instance = $caster instanceof Caster
33 | ? $caster
34 | : app($caster);
35 |
36 | return self::$enabled
37 | ? $instance->cast($target, new Properties($properties), $stub, $is_nested, $filter)
38 | : $properties;
39 | };
40 | }
41 |
42 | public static function disable(): void
43 | {
44 | self::$enabled = false;
45 | }
46 |
47 | public static function enable(): void
48 | {
49 | self::$enabled = true;
50 | }
51 |
52 | abstract public function cast($target, Properties $properties, Stub $stub, bool $is_nested, int $filter = 0): array;
53 | }
54 |
--------------------------------------------------------------------------------
/src/Casters/ContainerCaster.php:
--------------------------------------------------------------------------------
1 | cut += $properties->count();
24 | return [];
25 | }
26 |
27 | return $properties
28 | ->only($this->included)
29 | ->reorder($this->included)
30 | ->applyCutsToStub($stub, $properties)
31 | ->all();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Casters/CustomCaster.php:
--------------------------------------------------------------------------------
1 | operations)
33 | ->reduce(fn(Properties $properties, Closure $operation) => $operation($properties, $target), $properties)
34 | ->applyCutsToStub($stub, $properties)
35 | ->all();
36 | }
37 |
38 | public function reorder(array $rules): CustomCaster
39 | {
40 | $this->operations[] = fn(Properties $properties) => $properties->reorder($rules);
41 |
42 | return $this;
43 | }
44 |
45 | public function filter(callable $filter = null): CustomCaster
46 | {
47 | $this->operations[] = fn(Properties $properties) => $properties->filter($filter);
48 |
49 | return $this;
50 | }
51 |
52 | public function dynamic(string $key, Closure $callback): CustomCaster
53 | {
54 | $this->operations[] = fn(Properties $properties, $target) => $properties->putDynamic($key, $callback($target, $properties));
55 |
56 | return $this;
57 | }
58 |
59 | public function virtual(string $key, Closure $callback): CustomCaster
60 | {
61 | $this->operations[] = fn(Properties $properties, $target) => $properties->putVirtual($key, $callback($target, $properties));
62 |
63 | return $this;
64 | }
65 |
66 | public function only($keys): CustomCaster
67 | {
68 | $this->operations[] = fn(Properties $properties) => $properties->only($keys);
69 |
70 | return $this;
71 | }
72 |
73 | public function except($keys): CustomCaster
74 | {
75 | $this->operations[] = fn(Properties $properties) => $properties->except($keys);
76 |
77 | return $this;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Casters/DatabaseConnectionCaster.php:
--------------------------------------------------------------------------------
1 | getProtected('config'))) {
25 | return $properties->all();
26 | }
27 |
28 | $stub->cut += count($properties);
29 |
30 | return [
31 | Key::virtual('name') => $config['name'],
32 | Key::virtual('database') => $config['database'],
33 | Key::virtual('driver') => $config['driver'],
34 | ];
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Casters/HeaderBagCaster.php:
--------------------------------------------------------------------------------
1 | all())
25 | ->map(function(array $headers) {
26 | return 1 === count($headers)
27 | ? $headers[0]
28 | : $headers;
29 | })
30 | ->mapWithKeys(fn($value, $key) => [Key::virtual($key) => $value])
31 | ->all();
32 |
33 | $result[Key::protected('cacheControl')] = $properties[Key::protected('cacheControl')];
34 |
35 | return $result;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Casters/ModelCaster.php:
--------------------------------------------------------------------------------
1 | attributesToDynamicProperties($target),
36 | $this->virtualProperties($target),
37 | $this->cutProperties($properties, $stub, $is_nested)
38 | );
39 | }
40 |
41 | protected function attributesToDynamicProperties(Model $obj): array
42 | {
43 | return Properties::make($obj->getAttributes())
44 | ->sortKeys()
45 | ->reorder($this->attribute_order)
46 | ->mapWithKeys(fn($value, $key) => [Key::dynamic($key) => $value])
47 | ->all();
48 | }
49 |
50 | protected function virtualProperties(Model $obj): array
51 | {
52 | return [
53 | Key::virtual('isDirty()') => $obj->isDirty(),
54 | ];
55 | }
56 |
57 | protected function cutProperties(Properties $properties, Stub $stub, bool $is_nested): array
58 | {
59 | $keep = [
60 | 'exists',
61 | 'wasRecentlyCreated',
62 | Key::protected('relations'),
63 | ];
64 |
65 | if (! $is_nested) {
66 | $keep = array_merge($keep, [
67 | Key::protected('connection'),
68 | Key::protected('table'),
69 | Key::protected('original'),
70 | Key::protected('changes'),
71 | ]);
72 | }
73 |
74 | return $properties
75 | ->only($keep)
76 | ->reorder($keep)
77 | ->applyCutsToStub($stub, $properties)
78 | ->all();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Casters/ParameterBagCaster.php:
--------------------------------------------------------------------------------
1 | all())
25 | ->mapWithKeys(fn($value, $key) => [Key::virtual($key) => $value])
26 | ->all();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Casters/RequestCaster.php:
--------------------------------------------------------------------------------
1 | except(['userResolver', 'routeResolver'])
25 | ->filter()
26 | ->applyCutsToStub($stub, $properties)
27 | ->all();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Casters/ResponseCaster.php:
--------------------------------------------------------------------------------
1 | filter()
26 | ->applyCutsToStub($stub, $properties)
27 | ->all();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Casters/manifest.php:
--------------------------------------------------------------------------------
1 | mergeConfigFrom($this->packageConfigFile(), 'laravel-dumper');
12 |
13 | if ($this->isEnabledInCurrentEnvironment()) {
14 | require_once __DIR__.DIRECTORY_SEPARATOR.'helpers.php';
15 |
16 | $this->registerCasters();
17 | }
18 | }
19 |
20 | public function boot()
21 | {
22 | $this->publishes([
23 | $this->packageConfigFile() => $this->app->configPath('laravel-dumper.php'),
24 | ], ['laravel-dumper', 'laravel-dumper-config']);
25 | }
26 |
27 | protected function isEnabledInCurrentEnvironment(): bool
28 | {
29 | $environments = config('laravel-dumper.environments', ['local', 'testing']);
30 |
31 | return $this->app->environment($environments);
32 | }
33 |
34 | protected function registerCasters(): void
35 | {
36 | $casters = require __DIR__.'/Casters/manifest.php';
37 | foreach ($casters as $caster) {
38 | $caster::register($this->app);
39 | }
40 | }
41 |
42 | protected function packageConfigFile(): string
43 | {
44 | return dirname(__DIR__).DIRECTORY_SEPARATOR.'config.php';
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Support/Key.php:
--------------------------------------------------------------------------------
1 | cut += ($original->count() - $this->count());
26 |
27 | return $this;
28 | }
29 |
30 | public function cut($key, $default = null): CutStub
31 | {
32 | return new CutStub($this->get($key, $default));
33 | }
34 |
35 | public function cutProtected($key, $default = null): CutStub
36 | {
37 | return $this->cut(Key::protected($key), $default);
38 | }
39 |
40 | public function cutVirtual($key, $default = null): CutStub
41 | {
42 | return $this->cut(Key::virtual($key), $default);
43 | }
44 |
45 | public function cutDynamic($key, $default = null): CutStub
46 | {
47 | return $this->cut(Key::dynamic($key), $default);
48 | }
49 |
50 | public function get($key, $default = null)
51 | {
52 | $missing = new stdClass();
53 | foreach ($this->addPrefixes($key) as $prefixed_key) {
54 | $parameter = parent::get($prefixed_key, $missing);
55 | if ($missing !== $parameter) {
56 | return $parameter;
57 | }
58 | }
59 |
60 | return $default;
61 | }
62 |
63 | public function has($key)
64 | {
65 | $keys = is_array($key)
66 | ? $key
67 | : func_get_args();
68 |
69 | foreach ($keys as $value) {
70 | if (! $this->hasAny($this->addPrefixes($value))) {
71 | return false;
72 | }
73 | }
74 |
75 | return true;
76 | }
77 |
78 | public function hasAny($key)
79 | {
80 | if ($this->isEmpty()) {
81 | return false;
82 | }
83 |
84 | $keys = is_array($key)
85 | ? $key
86 | : func_get_args();
87 |
88 | foreach ($keys as $value) {
89 | if (array_key_exists($value, $this->items)) {
90 | return true;
91 | }
92 | }
93 |
94 | return false;
95 | }
96 |
97 | public function getProtected($key, $default = null)
98 | {
99 | return $this->get(Key::protected($key), $default);
100 | }
101 |
102 | public function getVirtual($key, $default = null)
103 | {
104 | return $this->get(Key::virtual($key), $default);
105 | }
106 |
107 | public function getDynamic($key, $default = null)
108 | {
109 | return $this->get(Key::dynamic($key), $default);
110 | }
111 |
112 | public function putProtected($key, $value): Properties
113 | {
114 | return $this->put(Key::protected($key), $value);
115 | }
116 |
117 | public function putVirtual($key, $value): Properties
118 | {
119 | return $this->put(Key::virtual($key), $value);
120 | }
121 |
122 | public function putDynamic($key, $value): Properties
123 | {
124 | return $this->put(Key::dynamic($key), $value);
125 | }
126 |
127 | public function copy($key, Properties $from, $default = null): Properties
128 | {
129 | return $this->put($key, $from->get($key, $default));
130 | }
131 |
132 | public function copyProtected($key, Properties $from, $default = null): Properties
133 | {
134 | return $this->copy(Key::protected($key), $from, $default);
135 | }
136 |
137 | public function copyVirtual($key, Properties $from, $default = null): Properties
138 | {
139 | return $this->copy(Key::virtual($key), $from, $default);
140 | }
141 |
142 | public function copyDynamic($key, Properties $from, $default = null): Properties
143 | {
144 | return $this->copy(Key::dynamic($key), $from, $default);
145 | }
146 |
147 | public function copyAndCut($key, Properties $from, $default = null): Properties
148 | {
149 | return $this->put($key, $from->cut($key, $default));
150 | }
151 |
152 | public function copyAndCutProtected($key, Properties $from, $default = null): Properties
153 | {
154 | return $this->copyAndCut(Key::protected($key), $from, $default);
155 | }
156 |
157 | public function copyAndCutVirtual($key, Properties $from, $default = null): Properties
158 | {
159 | return $this->copyAndCut(Key::virtual($key), $from, $default);
160 | }
161 |
162 | public function copyAndCutDynamic($key, Properties $from, $default = null): Properties
163 | {
164 | return $this->copyAndCut(Key::dynamic($key), $from, $default);
165 | }
166 |
167 | public function only($keys)
168 | {
169 | return $this->filter(function($value, $key) use ($keys) {
170 | return Str::is($keys, $key) || Str::is($keys, $this->stripPrefix($key));
171 | });
172 | }
173 |
174 | public function except($keys)
175 | {
176 | return $this->reject(function($value, $key) use ($keys) {
177 | return Str::is($keys, $key) || Str::is($keys, $this->stripPrefix($key));
178 | });
179 | }
180 |
181 | public function filter(callable $callback = null)
182 | {
183 | if (null === $callback) {
184 | $callback = static function($property) {
185 | if (is_array($property)) {
186 | return count($property);
187 | }
188 |
189 | if ($property instanceof Enumerable) {
190 | return $property->isNotEmpty();
191 | }
192 |
193 | return null !== $property;
194 | };
195 | }
196 |
197 | return parent::filter($callback);
198 | }
199 |
200 | public function reorder(array $rules): Properties
201 | {
202 | return $this->sortBy($this->getReorderCallback($rules));
203 | }
204 |
205 | protected function getReorderCallback(array $rules): Closure
206 | {
207 | $map = $this->createReorderMapFromRules($rules);
208 |
209 | return function($value, $key) use ($map) {
210 | $result = Arr::pull($map, '*');
211 |
212 | foreach ($map as $pattern => $position) {
213 | if ($key === $pattern || Str::is($pattern, $this->stripPrefix($key))) {
214 | $result = $position;
215 | }
216 | }
217 |
218 | return $result;
219 | };
220 | }
221 |
222 | protected function createReorderMapFromRules(array $rules): array
223 | {
224 | $rules = array_values($rules);
225 | $map = array_combine($rules, array_keys($rules));
226 |
227 | // Ensure that there's always a '*' pattern, defaulting to the end
228 | $map['*'] ??= count($map);
229 |
230 | return $map;
231 | }
232 |
233 | protected function stripPrefix(string $key): string
234 | {
235 | return str_replace($this->prefixes, '', $key);
236 | }
237 |
238 | protected function addPrefixes(string $key): array
239 | {
240 | if (Str::startsWith($key, $this->prefixes)) {
241 | return [$key];
242 | }
243 |
244 | return array_merge([$key], array_map(fn($prefix) => $prefix.$key, $this->prefixes));
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/src/autoload.php:
--------------------------------------------------------------------------------
1 | getDump($now);
19 |
20 | $this->assertStringStartsWith('Carbon\\Carbon', $dump);
21 | $this->assertStringContainsString('date: 2022-01-18 19:44:02.572622 America/New_York (-05:00)', $dump);
22 | $this->assertMatchesRegularExpression('/\s*…\d+\n}$/', $dump);
23 |
24 | $this->assertStringNotContainsString('localMacros', $dump);
25 | }
26 |
27 | public function test_package_can_be_disabled(): void
28 | {
29 | $this->withoutWritingDiffs();
30 |
31 | CustomCaster::for(static::class)
32 | ->only([])
33 | ->virtual('foo', fn() => 'bar');
34 |
35 | $getLineCount = fn() => substr_count($this->getDump($this), "\n") + 1;
36 |
37 | $this->assertEquals(4, $getLineCount());
38 |
39 | Caster::disable();
40 |
41 | $this->assertGreaterThan(100, $getLineCount());
42 |
43 | Caster::enable();
44 |
45 | $this->assertEquals(4, $getLineCount());
46 | }
47 |
48 | public function test_container(): void
49 | {
50 | $container = new Container();
51 |
52 | $container->bind(static::class, fn() => $this);
53 | $container->alias(static::class, 'bar');
54 | $container->extend('bar', fn() => $this);
55 | $container->make('bar');
56 |
57 | $dump = $this->getDump($container);
58 |
59 | $this->assertStringStartsWith('Illuminate\\Container\\Container', $dump);
60 | $this->assertStringContainsString('#bindings', $dump);
61 | $this->assertStringContainsString('#aliases', $dump);
62 | $this->assertStringContainsString('#resolved', $dump);
63 | $this->assertStringContainsString('#extenders', $dump);
64 | $this->assertStringContainsString(static::class, $dump);
65 |
66 | // These won't be true until I get a PR to Laravel
67 | // $this->assertMatchesRegularExpression('/\s*…\d+\n}$/', $dump);
68 | // $this->assertStringNotContainsString('#globalBeforeResolvingCallbacks', $dump);
69 | }
70 |
71 | public function test_container_nested(): void
72 | {
73 | $container = new Container();
74 |
75 | $expected = << Illuminate\Container\Container { …%d}
78 | ]
79 | EOD;
80 |
81 | $this->assertDumpMatchesFormat($expected, [$container]);
82 | }
83 |
84 | public function test_request(): void
85 | {
86 | $request = Request::create('/1');
87 |
88 | $dump = $this->getDump($request);
89 |
90 | $this->assertStringStartsWith('Illuminate\\Http\\Request {', $dump);
91 | $this->assertStringContainsString('+attributes', $dump);
92 | $this->assertStringContainsString('+request', $dump);
93 | $this->assertStringContainsString('+query', $dump);
94 | $this->assertStringContainsString('+server', $dump);
95 | $this->assertMatchesRegularExpression('/\s*…\d+\n}$/', $dump);
96 | }
97 |
98 | public function test_response(): void
99 | {
100 | $response = new Response('Hello world.');
101 |
102 | $dump = $this->getDump($response);
103 |
104 | $this->assertStringStartsWith('Illuminate\\Http\\Response {', $dump);
105 | $this->assertStringContainsString('+headers', $dump);
106 | $this->assertStringContainsString('#content: "Hello world."', $dump);
107 | $this->assertStringContainsString('#statusCode: 200', $dump);
108 | $this->assertMatchesRegularExpression('/\s*…\d+\n}$/', $dump);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/tests/CustomCasterTest.php:
--------------------------------------------------------------------------------
1 | only(['foo', 'nothing', 'nah'])
14 | ->dynamic('dyn', fn() => 'this is a dynamic prop')
15 | ->virtual('virt', fn() => 'this is a virtual prop')
16 | ->filter()
17 | ->reorder(['dyn', 'foo']);
18 |
19 | CustomCaster::for(MyOtherCustomObject::class)
20 | ->except('bar')
21 | ->virtual('foo', fn() => 'bar');
22 |
23 | $expected = <<assertDumpEquals($expected, new MyCustomObject());
33 |
34 | $expected = <<assertDumpEquals($expected, new MyOtherCustomObject());
41 | }
42 |
43 | public function test_custom_casters_cannot_be_registered(): void
44 | {
45 | $this->expectException(BadMethodCallException::class);
46 |
47 | CustomCaster::register($this->app);
48 | }
49 | }
50 |
51 | class MyCustomObject
52 | {
53 | protected $foo = 'foo';
54 |
55 | protected $bar = 'bar';
56 |
57 | protected $nothing = null;
58 |
59 | protected $nah = [];
60 | }
61 |
62 | class MyOtherCustomObject
63 | {
64 | protected $bar = 'bar';
65 | }
66 |
--------------------------------------------------------------------------------
/tests/DatabaseCasterTest.php:
--------------------------------------------------------------------------------
1 | 'Galahad']);
19 | $user = User::create(['name' => 'John', 'email' => 'foo@bar.com', 'company_id' => $company->id]);
20 | $user->setRelation('company', $company);
21 | $user->name = 'Chris';
22 |
23 | $timestamp = $now->format('Y-m-d H:i:s');
24 |
25 | $dump = $this->getDump($user);
26 |
27 | $this->assertStringStartsWith(User::class, $dump);
28 | $this->assertStringContainsString('id', $dump);
29 | $this->assertStringContainsString('company_id', $dump);
30 | $this->assertStringContainsString('email', $dump);
31 | $this->assertStringContainsString('name', $dump);
32 | $this->assertStringContainsString('isDirty()', $dump);
33 | $this->assertStringContainsString('exists', $dump);
34 | $this->assertStringContainsString('wasRecentlyCreated', $dump);
35 | $this->assertStringContainsString($timestamp, $dump);
36 | $this->assertMatchesRegularExpression('/\s*…\d+\n}$/', $dump);
37 |
38 | $this->assertStringNotContainsString('escapeWhenCastingToString', $dump);
39 | }
40 |
41 | public function test_query_builder(): void
42 | {
43 | $builder = DB::table('users')
44 | ->where('email', 'bogdan@foo.com')
45 | ->limit(10);
46 |
47 | $dump = $this->getDump($builder);
48 |
49 | $this->assertStringStartsWith('Illuminate\\Database\\Query\\Builder {', $dump);
50 | $this->assertStringContainsString('select * from "users" where "email" = \'bogdan@foo.com\' limit 10', $dump);
51 | $this->assertStringContainsString('#connection', $dump);
52 | $this->assertMatchesRegularExpression('/\s*…\d+\n}$/', $dump);
53 | }
54 |
55 | /** @see https://github.com/glhd/laravel-dumper/issues/6 */
56 | public function test_where_between_statement(): void
57 | {
58 | $builder = User::where('name', 'test')
59 | ->whereBetween('id', [1, 2]);
60 |
61 | $dump = $this->getDump($builder);
62 |
63 | $this->assertStringStartsWith('Illuminate\\Database\\Eloquent\\Builder {', $dump);
64 | $this->assertStringContainsString('select * from "users" where "name" = \'test\' and "id" between \'1\' and \'2\'', $dump);
65 | $this->assertStringContainsString('#connection', $dump);
66 | $this->assertStringContainsString('#model', $dump);
67 | $this->assertStringContainsString(User::class, $dump);
68 | $this->assertMatchesRegularExpression('/\s*…\d+\n}$/', $dump);
69 | }
70 |
71 | public function test_eloquent_builder(): void
72 | {
73 | $builder = User::query()
74 | ->with('company')
75 | ->where('email', 'bogdan@foo.com')
76 | ->limit(10);
77 |
78 | $dump = $this->getDump($builder);
79 |
80 | $this->assertStringStartsWith('Illuminate\\Database\\Eloquent\\Builder {', $dump);
81 | $this->assertStringContainsString('select * from "users" where "email" = \'bogdan@foo.com\' limit 10', $dump);
82 | $this->assertStringContainsString('#model', $dump);
83 | $this->assertStringContainsString('#eagerLoad', $dump);
84 | $this->assertMatchesRegularExpression('/\s*…\d+\n}$/', $dump);
85 | }
86 |
87 | public function test_eloquent_relation(): void
88 | {
89 | Company::create(['id' => 1, 'name' => 'Galahad']);
90 | $user = User::create(['id' => 1, 'name' => 'John', 'email' => 'foo@bar.com', 'company_id' => 1]);
91 |
92 | $dump = $this->getDump($user->company());
93 |
94 | $this->assertStringStartsWith('Illuminate\\Database\\Eloquent\\Relations\\BelongsTo {', $dump);
95 | $this->assertStringContainsString('select * from "companies" where "companies"."id" = \'1\'', $dump);
96 | $this->assertStringContainsString('#parent', $dump);
97 | $this->assertStringContainsString('#related', $dump);
98 | $this->assertStringContainsString(User::class, $dump);
99 | $this->assertStringContainsString(Company::class, $dump);
100 | $this->assertMatchesRegularExpression('/\s*…\d+\n}$/', $dump);
101 | }
102 |
103 | protected function defineDatabaseMigrations()
104 | {
105 | $this->loadMigrationsFrom(__DIR__.'/migrations');
106 | }
107 | }
108 |
109 | class User extends Model
110 | {
111 | protected $guarded = [];
112 |
113 | public function company()
114 | {
115 | return $this->belongsTo(Company::class);
116 | }
117 | }
118 |
119 | class Company extends Model
120 | {
121 | protected $guarded = [];
122 |
123 | public function users()
124 | {
125 | return $this->hasMany(User::class);
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/tests/PropertiesTest.php:
--------------------------------------------------------------------------------
1 | parameters = new Properties([
19 | Key::protected('protected') => 1,
20 | Key::virtual('virtual') => 1,
21 | Key::dynamic('dynamic') => 1,
22 | 'prefix_b' => 1,
23 | 'prefix_a' => 1,
24 | 'b_suffix' => 1,
25 | 'a_suffix' => 1,
26 | 'other' => 1,
27 | ]);
28 | }
29 |
30 | public function test_reordering_parameters(): void
31 | {
32 | $rules = [
33 | 'prefix_*',
34 | 'dynamic',
35 | 'virtual',
36 | '*',
37 | 'protected',
38 | '*_suffix',
39 | ];
40 |
41 | $reordered = $this->parameters->reorder($rules)->all();
42 |
43 | $this->assertEquals([
44 | 'prefix_b' => 1,
45 | 'prefix_a' => 1,
46 | Key::dynamic('dynamic') => 1,
47 | Key::virtual('virtual') => 1,
48 | 'other' => 1,
49 | Key::protected('protected') => 1,
50 | 'b_suffix' => 1,
51 | 'a_suffix' => 1,
52 | ], $reordered);
53 | }
54 |
55 | public function test_only_keeping_specific_parameters(): void
56 | {
57 | $subset = $this->parameters->only(['dynamic', '*_suffix'])->all();
58 |
59 | $this->assertEquals([
60 | Key::dynamic('dynamic') => 1,
61 | 'b_suffix' => 1,
62 | 'a_suffix' => 1,
63 | ], $subset);
64 | }
65 |
66 | public function test_excluding_specific_parameters(): void
67 | {
68 | $subset = $this->parameters->except(['dynamic', '*_suffix'])->all();
69 |
70 | $this->assertEquals([
71 | Key::protected('protected') => 1,
72 | Key::virtual('virtual') => 1,
73 | 'prefix_b' => 1,
74 | 'prefix_a' => 1,
75 | 'other' => 1,
76 | ], $subset);
77 | }
78 |
79 | public function test_has_method(): void
80 | {
81 | $this->assertTrue($this->parameters->has('protected'));
82 | $this->assertTrue($this->parameters->has(Key::protected('protected')));
83 | $this->assertTrue($this->parameters->has(Key::protected('protected'), 'prefix_b'));
84 | $this->assertTrue($this->parameters->has([Key::protected('protected'), 'prefix_b']));
85 |
86 | $this->assertFalse($this->parameters->has(Key::virtual('protected')));
87 | $this->assertFalse($this->parameters->has(Key::protected('protected'), 'foo'));
88 | $this->assertFalse($this->parameters->has([Key::protected('protected'), 'foo']));
89 |
90 | $this->assertTrue($this->parameters->hasAny(Key::protected('protected'), 'foo'));
91 | $this->assertTrue($this->parameters->hasAny([Key::protected('protected'), 'foo']));
92 | }
93 |
94 | public function test_get_methods(): void
95 | {
96 | $this->assertEquals(1, $this->parameters->getProtected('protected'));
97 | $this->assertNull($this->parameters->getProtected('virtual'));
98 |
99 | $this->assertEquals(1, $this->parameters->getVirtual('virtual'));
100 | $this->assertNull($this->parameters->getVirtual('protected'));
101 |
102 | $this->assertEquals(1, $this->parameters->getDynamic('dynamic'));
103 | $this->assertNull($this->parameters->getDynamic('protected'));
104 |
105 | $this->assertEquals(1, $this->parameters->get('protected'));
106 | $this->assertEquals(1, $this->parameters->get('virtual'));
107 | $this->assertEquals(1, $this->parameters->get('dynamic'));
108 | $this->assertEquals(1, $this->parameters->get('prefix_b'));
109 | $this->assertNull($this->parameters->get('missing param'));
110 | }
111 |
112 | public function test_cut_methods(): void
113 | {
114 | $this->assertEquals(1, $this->parameters->cutProtected('protected')->value);
115 | $this->assertNull($this->parameters->cutProtected('virtual')->value);
116 |
117 | $this->assertEquals(1, $this->parameters->cutVirtual('virtual')->value);
118 | $this->assertNull($this->parameters->cutVirtual('protected')->value);
119 |
120 | $this->assertEquals(1, $this->parameters->cutDynamic('dynamic')->value);
121 | $this->assertNull($this->parameters->cutDynamic('protected')->value);
122 |
123 | $this->assertEquals(1, $this->parameters->cut('protected')->value);
124 | $this->assertEquals(1, $this->parameters->cut('virtual')->value);
125 | $this->assertEquals(1, $this->parameters->cut('dynamic')->value);
126 | $this->assertEquals(1, $this->parameters->cut('prefix_b')->value);
127 | $this->assertNull($this->parameters->cut('missing param')->value);
128 | }
129 |
130 | public function test_copy_methods(): void
131 | {
132 | $destination = new Properties();
133 |
134 | $destination->copyProtected('protected', $this->parameters);
135 | $destination->copyVirtual('virtual', $this->parameters);
136 | $destination->copyDynamic('dynamic', $this->parameters);
137 | $destination->copy('prefix_b', $this->parameters);
138 |
139 | $this->assertEquals(1, $destination->get('protected'));
140 | $this->assertEquals(1, $destination->get('virtual'));
141 | $this->assertEquals(1, $destination->get('dynamic'));
142 | $this->assertEquals(1, $destination->get('prefix_b'));
143 | }
144 |
145 | public function test_copy_and_cut_methods(): void
146 | {
147 | $destination = new Properties();
148 |
149 | $destination->copyAndCutProtected('protected', $this->parameters);
150 | $destination->copyAndCutVirtual('virtual', $this->parameters);
151 | $destination->copyAndCutDynamic('dynamic', $this->parameters);
152 |
153 | $this->assertInstanceOf(CutStub::class, $destination->get('protected'));
154 | $this->assertInstanceOf(CutStub::class, $destination->get('virtual'));
155 | $this->assertInstanceOf(CutStub::class, $destination->get('dynamic'));
156 |
157 | $this->assertEquals(1, $destination->get('protected')->value);
158 | $this->assertEquals(1, $destination->get('virtual')->value);
159 | $this->assertEquals(1, $destination->get('dynamic')->value);
160 | }
161 |
162 | public function test_default_filtering(): void
163 | {
164 | $properties = new Properties([
165 | 'null' => null,
166 | 'empty array' => [],
167 | 'empty collection' => new Collection(),
168 | 'false' => false,
169 | 'value' => 1,
170 | ]);
171 |
172 | $this->assertEquals(['false' => false, 'value' => 1], $properties->filter()->all());
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | write_diff_if_configured = true;
29 | $this->diff_count = 0;
30 | }
31 |
32 | protected function withoutWritingDiffs(): self
33 | {
34 | $this->write_diff_if_configured = false;
35 |
36 | return $this;
37 | }
38 |
39 | protected function getPackageProviders($app)
40 | {
41 | return [
42 | LaravelDumperServiceProvider::class,
43 | ];
44 | }
45 |
46 | protected function getApplicationTimezone($app)
47 | {
48 | return 'America/New_York';
49 | }
50 |
51 | protected function getDump($data, $key = null, int $filter = 0): ?string
52 | {
53 | if (! $this->write_diff_if_configured || '1' !== getenv('WRITE_DIFFS')) {
54 | return $this->baseGetDump($data, $key, $filter);
55 | }
56 |
57 | Caster::disable();
58 | $before = $this->baseGetDump($data, $key, $filter);
59 |
60 | Caster::enable();
61 | $after = $this->baseGetDump($data, $key, $filter);
62 |
63 | $this->writeDiff($before, $after);
64 |
65 | return $after;
66 | }
67 |
68 | protected function writeDiff($before, $after)
69 | {
70 | if ($before === $after) {
71 | return;
72 | }
73 |
74 | $fs = new Filesystem();
75 | $path = __DIR__.'/../diffs';
76 |
77 | [$_, $_, $_, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
78 | $name = Str::of($caller['function'])->after('test_')->replace('_', '-');
79 |
80 | $this->diff_count++;
81 | if ($this->diff_count > 1) {
82 | $name .= "-{$this->diff_count}";
83 | }
84 |
85 | $before_file = "{$path}/{$name}.1.txt";
86 | $after_file = "{$path}/{$name}.2.txt";
87 |
88 | try {
89 | $fs->put($before_file, "{$before}\n");
90 | $fs->put($after_file, "{$after}\n");
91 |
92 | $diff = (new Process(['/usr/bin/diff', '-u', $before_file, $after_file]));
93 | $diff->run();
94 | $fs->put("{$path}/{$name}.diff", str_replace([$before_file, $after_file], ['Before', 'After'], $diff->getOutput()));
95 | } finally {
96 | $fs->delete([$before_file, $after_file]);
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/tests/migrations/2022_01_18_131057_create_test_tables.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
13 | $table->string('name');
14 | $table->string('email');
15 | $table->foreignId('company_id')->nullable();
16 | $table->timestamps();
17 | $table->softDeletes();
18 | });
19 |
20 | Schema::create('companies', function(Blueprint $table) {
21 | $table->bigIncrements('id');
22 | $table->string('name');
23 | $table->timestamps();
24 | $table->softDeletes();
25 | });
26 | }
27 |
28 | public function down()
29 | {
30 | Schema::dropIfExists('companies');
31 | Schema::dropIfExists('users');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------