├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── config.yml ├── dependabot.yml └── workflows │ ├── composer-normalize.yml │ ├── dependabot-auto-merge.yml │ ├── php-cs-fixer.yml │ ├── run-tests.yml │ ├── stale-issues.yml │ └── update-changelog.yml ├── .php_cs.dist ├── .php_cs.laravel ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── UPGRADING.md ├── art ├── README.md ├── logomark.png ├── logomark.svg ├── logomark@2x.png ├── logomark@3x.png ├── logomark@4x.png ├── palette │ ├── 100.png │ ├── 200.png │ ├── 300.png │ ├── 400.png │ ├── 500.png │ ├── 600.png │ ├── 700.png │ ├── 800.png │ └── 900.png └── socialcard.png ├── composer.json ├── config └── activitylog.php ├── database └── migrations │ ├── add_batch_uuid_column_to_activity_log_table.php.stub │ ├── add_event_column_to_activity_log_table.php.stub │ └── create_activity_log_table.php.stub └── src ├── ActivityLogStatus.php ├── ActivityLogger.php ├── ActivitylogServiceProvider.php ├── CauserResolver.php ├── CleanActivitylogCommand.php ├── Contracts ├── Activity.php └── LoggablePipe.php ├── EventLogBag.php ├── Exceptions ├── CouldNotLogActivity.php ├── CouldNotLogChanges.php └── InvalidConfiguration.php ├── Facades ├── Activity.php ├── CauserResolver.php └── LogBatch.php ├── LogBatch.php ├── LogOptions.php ├── Models └── Activity.php ├── PendingActivityLog.php ├── Traits ├── CausesActivity.php └── LogsActivity.php └── helpers.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: spatie 2 | custom: https://spatie.be/open-source/support-us 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug Report" 3 | about: Create a bug 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 | **To Reproduce** 14 | Things needed to reproduce the error. 15 | eg: Configuration, Migration, Model, Controller/Command 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Versions (please complete the following information)** 24 | - PHP: 25 | - Database: 26 | - Laravel: 27 | - Package: 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | 32 | **Exception** 33 | The thrown exception message. 34 | 35 | **Stack Trace** 36 | The full stack trace of the thrown exception. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 🚀 Feature Request 4 | url: https://github.com/spatie/laravel-activitylog/discussions/new?category_id=4507850 5 | about: Share ideas for new features 6 | - name: 🙏 Ask a Question 7 | url: https://github.com/spatie/laravel-activitylog/discussions/new?category_id=4507849 8 | about: Ask the community for help 9 | - name: 💖 Share some love 10 | url: https://github.com/spatie/laravel-activitylog/discussions/new?category_id=4507852 11 | about: Share gratitude with the team 12 | - name: 🚨 Report Security Vulnerability 13 | url: https://github.com/spatie/laravel-activitylog#security 14 | about: Please report security vulnerabilities by email to freek@spatie.be 15 | 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "composer" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | open-pull-requests-limit: 10 8 | reviewers: 9 | - "Gummibeer" 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "daily" 14 | reviewers: 15 | - "Gummibeer" 16 | -------------------------------------------------------------------------------- /.github/workflows/composer-normalize.yml: -------------------------------------------------------------------------------- 1 | name: normalize composer.json 2 | 3 | on: 4 | push: 5 | paths: 6 | - "composer.json" 7 | 8 | jobs: 9 | normalize: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Git checkout 13 | uses: actions/checkout@v3 14 | 15 | - name: normalize composer.json 16 | run: | 17 | composer global config --no-plugins allow-plugins.ergebnis/composer-normalize true 18 | composer global require ergebnis/composer-normalize 19 | composer normalize 20 | 21 | - uses: stefanzweifel/git-auto-commit-action@v5.2.0 22 | with: 23 | commit_message: normalize composer.json 24 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: dependabot-auto-merge 2 | on: pull_request_target 3 | 4 | permissions: 5 | pull-requests: write 6 | contents: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.actor == 'dependabot[bot]' }} 12 | steps: 13 | 14 | - name: Dependabot metadata 15 | id: metadata 16 | uses: dependabot/fetch-metadata@v2.4.0 17 | with: 18 | github-token: "${{ secrets.GITHUB_TOKEN }}" 19 | 20 | - name: Auto-merge Dependabot PRs for semver-minor updates 21 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} 22 | run: gh pr merge --auto --merge "$PR_URL" 23 | env: 24 | PR_URL: ${{github.event.pull_request.html_url}} 25 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 26 | 27 | - name: Auto-merge Dependabot PRs for semver-patch updates 28 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 29 | run: gh pr merge --auto --merge "$PR_URL" 30 | env: 31 | PR_URL: ${{github.event.pull_request.html_url}} 32 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 33 | -------------------------------------------------------------------------------- /.github/workflows/php-cs-fixer.yml: -------------------------------------------------------------------------------- 1 | name: Check & fix styling 2 | 3 | on: [push] 4 | 5 | jobs: 6 | php-cs-fixer: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v3 12 | with: 13 | ref: ${{ github.head_ref }} 14 | 15 | - name: Run PHP CS Fixer 16 | uses: docker://oskarstark/php-cs-fixer-ga:2.19.0 17 | with: 18 | args: --config=.php_cs.dist --allow-risky=yes 19 | 20 | - name: Commit changes 21 | uses: stefanzweifel/git-auto-commit-action@v5.2.0 22 | with: 23 | commit_message: Fix styling 24 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | jobs: 10 | test: 11 | if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | php: [8.3, 8.2, 8.1] 17 | laravel: ["^12.0", "^11.0", "^10.0", "^9.0"] 18 | dependency-version: [prefer-lowest, prefer-stable] 19 | exclude: 20 | - laravel: "^12.0" 21 | php: 8.1 22 | - laravel: "^11.0" 23 | php: 8.1 24 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} 25 | 26 | steps: 27 | - name: Checkout code 28 | uses: actions/checkout@v4 29 | 30 | - name: Setup PHP 31 | uses: shivammathur/setup-php@v2 32 | with: 33 | php-version: ${{ matrix.php }} 34 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 35 | coverage: none 36 | 37 | - name: Install dependencies 38 | run: | 39 | composer require "laravel/framework:${{ matrix.laravel }}" "nesbot/carbon:>=2.72" --dev --no-interaction --no-update 40 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction 41 | 42 | - name: Execute tests 43 | run: vendor/bin/pest 44 | -------------------------------------------------------------------------------- /.github/workflows/stale-issues.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9.1.0 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | stale-issue-message: "This issue is stale because it has been open 21 days with no activity. Remove stale label or comment or this will be closed in 7 days" 14 | stale-issue-label: "stale" 15 | exempt-issue-labels: "bug,enhancement,help wanted,next release,revisit for next major version" 16 | days-before-stale: 21 17 | days-before-close: 7 18 | -------------------------------------------------------------------------------- /.github/workflows/update-changelog.yml: -------------------------------------------------------------------------------- 1 | name: "Update Changelog" 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | update: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v3 14 | with: 15 | ref: main 16 | 17 | - name: Update Changelog 18 | uses: stefanzweifel/changelog-updater-action@v1 19 | with: 20 | latest-version: ${{ github.event.release.name }} 21 | release-notes: ${{ github.event.release.body }} 22 | 23 | - name: Commit updated CHANGELOG 24 | uses: stefanzweifel/git-auto-commit-action@v5.2.0 25 | with: 26 | branch: main 27 | commit_message: Update CHANGELOG 28 | file_pattern: CHANGELOG.md 29 | -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | notPath('bootstrap/*') 5 | ->notPath('storage/*') 6 | ->notPath('resources/view/mail/*') 7 | ->in([ 8 | __DIR__.'/src', 9 | __DIR__.'/tests', 10 | ]) 11 | ->name('*.php') 12 | ->notName('*.blade.php') 13 | ->ignoreDotFiles(true) 14 | ->ignoreVCS(true); 15 | 16 | return \PhpCsFixer\Config::create() 17 | ->setRules(array_merge(require '.php_cs.laravel', [ 18 | '@PSR2' => true, 19 | '@PSR12' => true, 20 | 'no_unused_imports' => true, 21 | 'phpdoc_to_comment' => false, 22 | 'phpdoc_order' => true, 23 | 'phpdoc_separation' => true, 24 | 'simplified_null_return' => false, 25 | ])) 26 | ->setLineEnding("\n") 27 | ->setIndent(str_repeat(' ', 4)) 28 | ->setUsingCache(false) 29 | ->setRiskyAllowed(true) 30 | ->setFinder($finder); 31 | -------------------------------------------------------------------------------- /.php_cs.laravel: -------------------------------------------------------------------------------- 1 | ['syntax' => 'short'], 9 | 'binary_operator_spaces' => [ 10 | 'default' => 'single_space', 11 | 'operators' => ['=>' => null] 12 | ], 13 | 'blank_line_after_namespace' => true, 14 | 'blank_line_after_opening_tag' => true, 15 | 'blank_line_before_statement' => [ 16 | 'statements' => ['return'] 17 | ], 18 | 'braces' => true, 19 | 'cast_spaces' => true, 20 | 'class_attributes_separation' => [ 21 | 'elements' => ['method'] 22 | ], 23 | 'class_definition' => true, 24 | 'concat_space' => [ 25 | 'spacing' => 'none' 26 | ], 27 | 'declare_equal_normalize' => true, 28 | 'elseif' => true, 29 | 'encoding' => true, 30 | 'full_opening_tag' => true, 31 | 'fully_qualified_strict_types' => true, // added by Shift 32 | 'function_declaration' => true, 33 | 'function_typehint_space' => true, 34 | 'heredoc_to_nowdoc' => true, 35 | 'include' => true, 36 | 'increment_style' => ['style' => 'post'], 37 | 'indentation_type' => true, 38 | 'linebreak_after_opening_tag' => true, 39 | 'line_ending' => true, 40 | 'lowercase_cast' => true, 41 | 'lowercase_constants' => true, 42 | 'lowercase_keywords' => true, 43 | 'lowercase_static_reference' => true, // added from Symfony 44 | 'magic_method_casing' => true, // added from Symfony 45 | 'magic_constant_casing' => true, 46 | 'method_argument_space' => true, 47 | 'native_function_casing' => true, 48 | 'no_alias_functions' => true, 49 | 'no_extra_blank_lines' => [ 50 | 'tokens' => [ 51 | 'extra', 52 | 'throw', 53 | 'use', 54 | 'use_trait', 55 | ] 56 | ], 57 | 'no_blank_lines_after_class_opening' => true, 58 | 'no_blank_lines_after_phpdoc' => true, 59 | 'no_closing_tag' => true, 60 | 'no_empty_phpdoc' => true, 61 | 'no_empty_statement' => true, 62 | 'no_leading_import_slash' => true, 63 | 'no_leading_namespace_whitespace' => true, 64 | 'no_mixed_echo_print' => [ 65 | 'use' => 'echo' 66 | ], 67 | 'no_multiline_whitespace_around_double_arrow' => true, 68 | 'multiline_whitespace_before_semicolons' => [ 69 | 'strategy' => 'no_multi_line' 70 | ], 71 | 'no_short_bool_cast' => true, 72 | 'no_singleline_whitespace_before_semicolons' => true, 73 | 'no_spaces_after_function_name' => true, 74 | 'no_spaces_around_offset' => true, 75 | 'no_spaces_inside_parenthesis' => true, 76 | 'no_trailing_comma_in_list_call' => true, 77 | 'no_trailing_comma_in_singleline_array' => true, 78 | 'no_trailing_whitespace' => true, 79 | 'no_trailing_whitespace_in_comment' => true, 80 | 'no_unneeded_control_parentheses' => true, 81 | 'no_unreachable_default_argument_value' => true, 82 | 'no_useless_return' => true, 83 | 'no_whitespace_before_comma_in_array' => true, 84 | 'no_whitespace_in_blank_line' => true, 85 | 'normalize_index_brace' => true, 86 | 'not_operator_with_successor_space' => true, 87 | 'object_operator_without_whitespace' => true, 88 | 'ordered_imports' => ['sortAlgorithm' => 'alpha'], 89 | 'phpdoc_indent' => true, 90 | 'phpdoc_inline_tag' => true, 91 | 'phpdoc_no_access' => true, 92 | 'phpdoc_no_package' => true, 93 | 'phpdoc_no_useless_inheritdoc' => true, 94 | 'phpdoc_scalar' => true, 95 | 'phpdoc_single_line_var_spacing' => true, 96 | 'phpdoc_summary' => true, 97 | 'phpdoc_to_comment' => true, 98 | 'phpdoc_trim' => true, 99 | 'phpdoc_types' => true, 100 | 'phpdoc_var_without_name' => true, 101 | 'psr4' => true, 102 | 'self_accessor' => true, 103 | 'short_scalar_cast' => true, 104 | 'simplified_null_return' => true, 105 | 'single_blank_line_at_eof' => true, 106 | 'single_blank_line_before_namespace' => true, 107 | 'single_class_element_per_statement' => true, 108 | 'single_import_per_statement' => true, 109 | 'single_line_after_imports' => true, 110 | 'single_line_comment_style' => [ 111 | 'comment_types' => ['hash'] 112 | ], 113 | 'single_quote' => true, 114 | 'space_after_semicolon' => true, 115 | 'standardize_not_equals' => true, 116 | 'switch_case_semicolon_to_colon' => true, 117 | 'switch_case_space' => true, 118 | 'ternary_operator_spaces' => true, 119 | 'trailing_comma_in_multiline_array' => true, 120 | 'trim_array_spaces' => true, 121 | 'unary_operator_spaces' => true, 122 | 'visibility_required' => [ 123 | 'elements' => ['method', 'property'] 124 | ], 125 | 'whitespace_after_comma_in_array' => true, 126 | ]; 127 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `spatie/laravel-activitylog` will be documented in this file 4 | 5 | ## 4.10.1 - 2025-02-10 6 | 7 | ### What's Changed 8 | 9 | * Laravel 12.x Support by @erikn69 in https://github.com/spatie/laravel-activitylog/pull/1370 10 | 11 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.10.0...4.10.1 12 | 13 | ## 4.10.0 - 2025-02-10 14 | 15 | ### What's Changed 16 | 17 | * Bump stefanzweifel/git-auto-commit-action from 5.0.1 to 5.1.0 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1365 18 | * Bump actions/stale from 9.0.0 to 9.1.0 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1367 19 | * Bump dependabot/fetch-metadata from 2.2.0 to 2.3.0 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1368 20 | * Activity Facade by @stevebauman in https://github.com/spatie/laravel-activitylog/pull/1372 21 | 22 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.9.1...4.10.0 23 | 24 | ## 4.9.1 - 2024-11-18 25 | 26 | ### What's Changed 27 | 28 | * Update using-multiple-logs.md by @tobischulz in https://github.com/spatie/laravel-activitylog/pull/1345 29 | * fix: php 8.4 deprecation warnings by @ashleyshenton in https://github.com/spatie/laravel-activitylog/pull/1351 30 | 31 | ### New Contributors 32 | 33 | * @tobischulz made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1345 34 | * @ashleyshenton made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1351 35 | 36 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.9.0...4.9.1 37 | 38 | ## 4.9.0 - 2024-10-18 39 | 40 | ### What's Changed 41 | 42 | * Bump dependabot/fetch-metadata from 1.6.0 to 2.1.0 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1298 43 | * Bump stefanzweifel/git-auto-commit-action from 4.15.4 to 5.0.1 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1293 44 | * Corrected the link to the event logging example by @makaronnik in https://github.com/spatie/laravel-activitylog/pull/1300 45 | * Bump dependabot/fetch-metadata from 2.1.0 to 2.2.0 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1315 46 | * Update batch-logs.md by @jesseschutt in https://github.com/spatie/laravel-activitylog/pull/1320 47 | * Adjust PHPDoc type for causer and subject by @gtg-bantonio in https://github.com/spatie/laravel-activitylog/pull/1321 48 | * Add missing return type by @dwightwatson in https://github.com/spatie/laravel-activitylog/pull/1330 49 | * Possibility to define table name in environment file by @edwinvdpol in https://github.com/spatie/laravel-activitylog/pull/1334 50 | 51 | ### New Contributors 52 | 53 | * @makaronnik made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1300 54 | * @jesseschutt made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1320 55 | * @gtg-bantonio made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1321 56 | * @edwinvdpol made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1334 57 | 58 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.8.0...4.9.0 59 | 60 | ## 4.8.0 - 2024-03-08 61 | 62 | ### What's Changed 63 | 64 | * Bump dependabot/fetch-metadata from 1.3.6 to 1.4.0 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1183 65 | * Bump dependabot/fetch-metadata from 1.4.0 to 1.5.0 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1195 66 | * Bump dependabot/fetch-metadata from 1.5.0 to 1.5.1 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1196 67 | * Bump dependabot/fetch-metadata from 1.5.1 to 1.6.0 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1205 68 | * Add a missing apostrophe by @dwightwatson in https://github.com/spatie/laravel-activitylog/pull/1249 69 | * Bump actions/stale from 6.0.1 to 9.0.0 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1250 70 | * Bump actions/cache from 3 to 4 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1263 71 | * Add more detail to manipulating changes by @tonypartridge in https://github.com/spatie/laravel-activitylog/pull/1268 72 | * feat!: add laravel 11 support by @StevePorter92 in https://github.com/spatie/laravel-activitylog/pull/1276 73 | 74 | ### New Contributors 75 | 76 | * @dwightwatson made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1249 77 | * @tonypartridge made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1268 78 | * @StevePorter92 made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1276 79 | 80 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.7.3...4.8.0 81 | 82 | ## 4.7.3 - 2023-01-25 83 | 84 | ### What's Changed 85 | 86 | - Add Dependabot Automation by @patinthehat in https://github.com/spatie/laravel-activitylog/pull/1128 87 | - Add PHP 8.2 Support by @patinthehat in https://github.com/spatie/laravel-activitylog/pull/1129 88 | - Bump dependabot/fetch-metadata from 1.3.5 to 1.3.6 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1156 89 | - Fix tests badge by @erikn69 in https://github.com/spatie/laravel-activitylog/pull/1153 90 | - Laravel 10.x support by @erikn69 in https://github.com/spatie/laravel-activitylog/pull/1152 91 | - Fix for replacePlaceholders with ending dot by @Stefan-Dressler in https://github.com/spatie/laravel-activitylog/pull/1154 92 | 93 | ### New Contributors 94 | 95 | - @patinthehat made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1128 96 | - @erikn69 made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1153 97 | - @Stefan-Dressler made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1154 98 | 99 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.7.2...4.7.3 100 | 101 | ## v4.7.2 - 2022-11-14 102 | 103 | ### What's Changed 104 | 105 | - Bump actions/checkout from 2 to 3 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1117 106 | - Bump actions/stale from 2.0.0 to 6.0.1 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1118 107 | - Bump stefanzweifel/git-auto-commit-action from 4.0.0 to 4.15.4 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1119 108 | - Add missing properties to Activity model by @AndreasHerss in https://github.com/spatie/laravel-activitylog/pull/1101 109 | - Bump actions/cache from 2 to 3 by @dependabot in https://github.com/spatie/laravel-activitylog/pull/1120 110 | - Fix enum casting by @Gummibeer in https://github.com/spatie/laravel-activitylog/pull/1121 111 | 112 | ### New Contributors 113 | 114 | - @dependabot made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1117 115 | - @AndreasHerss made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1101 116 | 117 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.7.1...4.7.2 118 | 119 | ## v4.7.1 - 2022-11-11 120 | 121 | ### What's Changed 122 | 123 | - Fix nullable custom properties in PHP 8.0 by @stevebauman in https://github.com/spatie/laravel-activitylog/pull/1115 124 | 125 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.7.0...4.7.1 126 | 127 | ## v4.7.0 - 2022-11-10 128 | 129 | ### What's Changed 130 | 131 | - Fix indentation by @mouadziani in https://github.com/spatie/laravel-activitylog/pull/1092 132 | - Support non backed enum & php 8.1 by @pemudakoding in https://github.com/spatie/laravel-activitylog/pull/1110 133 | 134 | ### New Contributors 135 | 136 | - @mouadziani made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1092 137 | - @pemudakoding made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1110 138 | 139 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.6.0...4.7.0 140 | 141 | ## v4.6.0 - 2022-09-22 142 | 143 | ### What's Changed 144 | 145 | - Add a default value to `getExtraProperty()` by @grantholle in https://github.com/spatie/laravel-activitylog/pull/1090 146 | 147 | ### New Contributors 148 | 149 | - @grantholle made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1090 150 | 151 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.5.3...4.6.0 152 | 153 | ## 4.5.3 - 2022-05-31 154 | 155 | - Fix default auth guard for causer - [#1053](https://github.com/spatie/laravel-activitylog/pull/1053) 156 | 157 | ## 4.5.2 - 2022-04-21 158 | 159 | - Fix placeholder resolving - [#1038](https://github.com/spatie/laravel-activitylog/pull/1038) 160 | 161 | ## 4.5.1 - 2022-04-07 162 | 163 | - [Use scoped instances of instead of singletons (Octane support)](https://github.com/spatie/laravel-activitylog/commit/0d0075b9c56ed0c282f59037e71cdaa6a052d336) 164 | 165 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.5.0...4.5.1 166 | 167 | ## 4.5.0 - 2022-04-07 168 | 169 | ## What's Changed 170 | 171 | - Add `Conditionable` trait to `ActivityLogger` by @usernotnull in https://github.com/spatie/laravel-activitylog/pull/997 172 | 173 | ## New Contributors 174 | 175 | - @usernotnull made their first contribution in https://github.com/spatie/laravel-activitylog/pull/997 176 | 177 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.4.3...4.5.0 178 | 179 | ## 4.4.3 - 2022-04-07 180 | 181 | ## What's Changed 182 | 183 | - Fix model serialization when using `LogsActivity` with `setDescriptionForEvent()` by @stevebauman in https://github.com/spatie/laravel-activitylog/pull/977 184 | - Fix activirt logging on model restore (#895) by @kryptamine in https://github.com/spatie/laravel-activitylog/pull/1000 185 | - Fix nullable log names by @stevebauman in https://github.com/spatie/laravel-activitylog/pull/1029 186 | - Fix `tapActivity` when manually creating activity logs by @FrancisMawn in https://github.com/spatie/laravel-activitylog/pull/1031 187 | 188 | ## New Contributors 189 | 190 | - @stevebauman made their first contribution in https://github.com/spatie/laravel-activitylog/pull/977 191 | - @kryptamine made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1000 192 | - @FrancisMawn made their first contribution in https://github.com/spatie/laravel-activitylog/pull/1031 193 | 194 | **Full Changelog**: https://github.com/spatie/laravel-activitylog/compare/4.4.2...4.4.3 195 | 196 | ## 4.4.2 - 2022-03-07 197 | 198 | - [#1018](https://github.com/spatie/laravel-activitylog/pull/1018) 199 | 200 | ## 4.4.1 - 2022-03-04 201 | 202 | - https://github.com/spatie/laravel-activitylog/pull/956 203 | 204 | ## 4.4.0 - 2022-01-12 205 | 206 | - allow Laravel 9 207 | 208 | ## 4.3.1 - 2021-10-20 209 | 210 | - Fix hardcoded `deleted_at` column usage - [#965](https://github.com/spatie/laravel-activitylog/issues/965) 211 | 212 | ## 4.3.0 - 2021-10-20 213 | 214 | - Add `\Spatie\Activitylog\LogOptions::useAttributeRawValues()` to log uncasted attributes - [#972](https://github.com/spatie/laravel-activitylog/issues/972) 215 | 216 | ## 4.2.0 - 2021-10-06 217 | 218 | - Add `immutable_date` cast support - [#969](https://github.com/spatie/laravel-activitylog/issues/969) 219 | 220 | ## 4.1.1 - 2021-07-23 221 | 222 | - Fix migration stub names - [#914](https://github.com/spatie/laravel-activitylog/issues/914) 223 | 224 | ## 4.1.0 - 2021-07-23 225 | 226 | - Add `\Spatie\Activitylog\LogBatch::setBatch(string $uuid)` method to keep batches across requests or multiple jobs - [#918](https://github.com/spatie/laravel-activitylog/issues/918) 227 | 228 | ## 4.0.0 - 2021-05-04 229 | 230 | PR: [#787](https://github.com/spatie/laravel-activitylog/pull/787) 231 | Special thanks to [Ahmed Nagi](https://github.com/nagi1). 232 | 233 | - Drop Laravel 6 and 7 support. 234 | 235 | - Drop PHP 7.x support. 236 | 237 | - Add `LogOptions` configuration object to replace all configuration properties. 238 | 239 | - Add ability to batch activity logs [#560](https://github.com/spatie/laravel-activitylog/issues/560) 240 | 241 | - Add Pipeline to customize logged changes data. 242 | 243 | - - Deep diff array/JSON sub-keys and respect for only-dirty, no-empty ... [#692](https://github.com/spatie/laravel-activitylog/issues/692) using new pipeline. See implementation in the tests. 244 | 245 | - 246 | - 247 | - 248 | - 249 | - 250 | - 251 | - 252 | - 253 | - 254 | - 255 | - 256 | - Implement a `CauserResolver` to define causer for current runtime [#582](https://github.com/spatie/laravel-activitylog/issues/582). 257 | 258 | 259 | ## 3.17.0 - 2021-03-02 260 | 261 | - drop PHP 7.2 support - [#855](https://github.com/spatie/laravel-activitylog/pull/855) 262 | 263 | ## 3.16.1 - 2020-11-03 264 | 265 | - add PHP 8.0 support - [#806](https://github.com/spatie/laravel-activitylog/pull/806) 266 | 267 | ## 3.16.0 - 2020-09-16 268 | 269 | - use `nullableMorphs()` in default migration - [#707](https://github.com/spatie/laravel-activitylog/pull/707) 270 | - add support for snake and camel cased related model attribute logging - [#721](https://github.com/spatie/laravel-activitylog/pull/721) 271 | 272 | ## 3.15.0 - 2020-09-14 273 | 274 | - Add multiple/chained relation attribute logging support - [#784](https://github.com/spatie/laravel-activitylog/pull/784) 275 | 276 | ## 3.14.3 - 2020-09-09 277 | 278 | - Add support for Laravel 8 279 | 280 | ## 3.14.2 - 2020-05-19 281 | 282 | - fix `retrieved` event logging 283 | 284 | ## 3.14.1 - 2020-03-23 285 | 286 | - revert breaking changes in `v3.14.0` 287 | 288 | ## 3.14.0 - 2020-03-23 - BC 289 | 290 | Please use `v3.14.1` instead - this release is breaking because of the new column. There is also a `v4.0.0-rc.1` release that equals to this one. 291 | 292 | - add `\Spatie\Activitylog\ActivityLogger::event()` method and column [#702](https://github.com/spatie/laravel-activitylog/pull/702) 293 | 294 | ## 3.13.0 - 2020-03-13 295 | 296 | - add `\Spatie\Activitylog\ActivityLogger::withoutLogs()` method [#695](https://github.com/spatie/laravel-activitylog/pull/695) 297 | 298 | ## 3.12.0 - 2020-03-13 299 | 300 | - respect custom date casts [#627](https://github.com/spatie/laravel-activitylog/pull/627) 301 | 302 | ## 3.11.4 - 2020-03-11 303 | 304 | - remove `spatie/string` dependency [#690](https://github.com/spatie/laravel-activitylog/pull/690) 305 | 306 | ## 3.11.3 - 2020-03-10 307 | 308 | - fix performance issue around global vs model log disabling [#682](https://github.com/spatie/laravel-activitylog/pull/682) 309 | 310 | ## 3.11.2 - 2020-03-10 311 | 312 | - fix Laravel 7 array/json casted attributes [#680](https://github.com/spatie/laravel-activitylog/pull/680) 313 | 314 | ## 3.11.1 - 2020-03-02 315 | 316 | - fix requirements 317 | 318 | ## 3.11.0 - 2020-03-02 319 | 320 | - add support for Laravel 7 321 | 322 | ## 3.10.0 - 2020-02-22 323 | 324 | - add ability to manually set created at date - [#622](https://github.com/spatie/laravel-activitylog/pull/622) 325 | 326 | ## 3.9.2 - 2020-02-04 327 | 328 | - drop support for Laravel 5 329 | 330 | ## 3.9.1 - 2019-10-15 331 | 332 | - fix default database connection - [#616](https://github.com/spatie/laravel-activitylog/pull/616) 333 | 334 | ## 3.9.0 - 2019-10-06 335 | 336 | - add anonymous causer with `null` value - [#605](https://github.com/spatie/laravel-activitylog/pull/605) 337 | - fix relationships to allow snake case keys - [#602](https://github.com/spatie/laravel-activitylog/pull/602) 338 | - add JOSN sub-key attribute logging - [#601](https://github.com/spatie/laravel-activitylog/pull/601) 339 | 340 | ## 3.8.0 - 2019-09-04 341 | 342 | - add support for Laravel 6 343 | - change fields with value `null` to be strictly compared when logging dirty fields [#453](https://github.com/spatie/laravel-activitylog/pull/453) 344 | - add composite indexes for subject and causer to migration 345 | 346 | ## 3.7.2 - 2019-08-28 347 | 348 | - do not export docs folder 349 | 350 | ## 3.7.1 - 2019-07-24 351 | 352 | - fix default database connection env var 353 | 354 | ## 3.7.0 - 2019-07-23 355 | 356 | - add database connection to configuration `activitylog.database_connection` and `ACTIVITY_LOGGER_DB_CONNECTION` env var [#568](https://github.com/spatie/laravel-activitylog/pull/568) 357 | 358 | ## 3.6.3 - 2019-07-23 359 | 360 | - fix deprecated `array_` helper [#569](https://github.com/spatie/laravel-activitylog/pull/569) 361 | 362 | ## 3.6.2 - 2019-07-16 363 | 364 | - fix existing description [#563](https://github.com/spatie/laravel-activitylog/pull/563) 365 | 366 | ## 3.6.1 - 2019-05-29 367 | 368 | - fix nullable date attributes [#546](https://github.com/spatie/laravel-activitylog/pull/546) 369 | 370 | ## 3.6.0 - 2019-05-28 371 | 372 | - update `properties` column type from `text` to `json` [#525](https://github.com/spatie/laravel-activitylog/pull/525) 373 | - update `subject_id` and `causer_id` column type from `integer` to `big_integer` and `unsigned` [#527](https://github.com/spatie/laravel-activitylog/pull/527) 374 | - fix attribute getter support in `DetectsChanges` trait [#534](https://github.com/spatie/laravel-activitylog/pull/534) 375 | - fix old attributes retrieval in `DetectsChanges` trait [#537](https://github.com/spatie/laravel-activitylog/pull/537) 376 | - clean up old attributes in `DetectsChanges` trait [#538](https://github.com/spatie/laravel-activitylog/pull/538) 377 | 378 | ## 3.5.0 - 2019-04-15 379 | 380 | - add days option to clean command [#497](https://github.com/spatie/laravel-activitylog/pull/497) 381 | - add `LogsActivity::$submitEmptyLogs` [#514](https://github.com/spatie/laravel-activitylog/pull/514) 382 | 383 | ## 3.4.0 - 2019-04-09 384 | 385 | - use `Illuminate\Contracts\Config\Repository` instead of `Illuminate\Config\Repository` [#505](https://github.com/spatie/laravel-activitylog/pull/505) 386 | - fix `logChanges()` [#512](https://github.com/spatie/laravel-activitylog/pull/512) 387 | 388 | ## 3.3.0 - 2019-04-08 389 | 390 | - drop support for Laravel 5.7 and lower 391 | - drop support for PHP 7.1 and lower 392 | 393 | ## 3.2.2 - 2019-02-27 394 | 395 | - add support for Laravel 5.8 396 | - fix logging hidden attributes 397 | - fix logging for a causer model without a provider 398 | - add code coverage reporting for repository 399 | 400 | ## 3.2.1 - 2019-02-01 401 | 402 | - use Str:: and Arr:: instead of helper methods 403 | 404 | ## 3.2.0 - 2019-01-29 405 | 406 | - add `ActivityLogger::tap()` method 407 | - add `LogsActivity::tapActivity()` method 408 | - the `ActivityLogger` will work on an activity model instance instead of cache variables 409 | 410 | ## 3.1.2 - 2018-10-18 411 | 412 | - add `shouldLogUnguarded()` method 413 | - fix typo in methodname `shouldLogOnlyDirty()` 414 | 415 | ## 3.1.1 - 2018-10-17 416 | 417 | - fix `$logUnguarded` 418 | 419 | ## 3.1.0 - 2018-10-17 420 | 421 | - add `$logUnguarded` 422 | 423 | ## 3.0.0 - 2018-10-16 424 | 425 | - the preferred way to get changes on an `Activity` model is through the `changes` property instead of the `changes()` function 426 | - the `activity` relation of the `CausesActivity` trait has been renamed to `actions` 427 | - the `activity` relation of the `LogsActivity` trait has been renamed to `activities` 428 | - the deprecated `loggedActivity` relation has been removed 429 | - the `HasActivity` trait has been removed. 430 | - fix for setting a custom table name for the `Activity` model via the `$table` property 431 | - support for PHP 7.0 has been dropped 432 | 433 | ## 2.8.4. - 2018-09-23 434 | 435 | - improve migration 436 | 437 | ## 2.8.3 - 2018-09-01 438 | 439 | - add support for L5.7 440 | 441 | ## 2.8.2 - 2018-07-28 442 | 443 | - allow `null` to be passed to `causedBy` 444 | 445 | ## 2.8.1 - 2018-07-28 446 | 447 | - make sure a fresh instance of `ActivityLogger` is used 448 | 449 | ## 2.8.0 - 2018-07-21 450 | 451 | - add `enableLogging()` and `disableLogging()` 452 | 453 | ## 2.7.0 - 2018-06-18 454 | 455 | - add ability to ignore changes to attributes specified in `$logAttributesToIgnore` 456 | 457 | ## 2.6.0 - 2018-04-03 458 | 459 | - add `table_name` config option 460 | 461 | ## 2.5.1 - 2018-02-11 462 | 463 | - improve support for soft deletes 464 | 465 | ## 2.5.0 - 2018-02-09 466 | 467 | - allow model to override the default log name 468 | 469 | ## 2.4.2 - 2018-02-08 470 | 471 | - add compatibility with L5.6 472 | 473 | ## 2.4.1 - 2018-01-20 474 | 475 | - use a `text` column for `description` 476 | 477 | ## 2.4.0 - 2018-01-20 478 | 479 | - add `HasActivity` 480 | 481 | ## 2.3.2 - 2017-12-13 482 | 483 | - fix bugs concerning `attributesToBeLogged` 484 | 485 | ## 2.3.1 - 2017-11-13 486 | 487 | - allow nullable relation when using `logChanges` 488 | 489 | ## 2.3.0 - 2017-11-07 490 | 491 | - add a `log` argument to `activitylog:clean` 492 | 493 | ## 2.2.0 - 2017-10-16 494 | 495 | - add support for logging all changed attributes using `*` 496 | 497 | ## 2.1.2 - 2017-09-28 498 | 499 | - fix for logging changes attributes when deleting soft deletable models 500 | 501 | ## 2.1.1 - 2017-09-12 502 | 503 | - make sure `properties` always is a collection 504 | 505 | ## 2.1.0 - 2017-09-19 506 | 507 | - added support for logging fillable attributes 508 | 509 | ## 2.0.0 - 2017-08-30 510 | 511 | - added support for Laravel 5.5, dropped support for older laravel versions 512 | - renamed config file from `laravel-activitylog` to `activitylog` 513 | - rename `getChangesAttribute` function to `changes` so it doesn't conflict with Laravel's native functionality 514 | 515 | ## 1.16.0 - 2017-06-28 516 | 517 | - added `enableLogging` and `disableLogging` 518 | 519 | ## 1.15.5 - 2017-08-08 520 | 521 | - fix model scope 522 | 523 | ## 1.15.4 - 2017-08-05 524 | 525 | - fix detecting `SoftDeletes` 526 | 527 | ## 1.15.3 - 2017-06-23 528 | 529 | - fix for when there is no 'web' guard 530 | 531 | ## 1.15.2 - 2017-06-15 532 | 533 | - fixes errors in `DetectsChanges` 534 | 535 | ## 1.15.1 - 2017-04-28 536 | 537 | - fixes error in `DetectsChanges` 538 | 539 | ## 1.15.0 - 2017-04-28 540 | 541 | - add compatibility with L5.1 and L5.2 542 | 543 | ## 1.14.0 - 2017-04-16 544 | 545 | - add support array/collection casted attributes when using `logDirtyOnly` 546 | 547 | ## 1.13.0 - 2017-04-16 548 | 549 | - add `logDirtyOnly` 550 | 551 | ## 1.12.2 - 2017-03-22 552 | 553 | - fix a bug where changes to a related model would not be logged 554 | 555 | ## 1.12.1 - 2017-02-12 556 | 557 | - avoid PHP error when dealing with placeholders that cannot be filled 558 | 559 | ## 1.12.0 - 2017-02-04 560 | 561 | - drop support for L5.2 and lower 562 | - add ability to log attributes of related models 563 | 564 | ## 1.11.0 - 2017-01-23 565 | 566 | - add support for L5.4 567 | 568 | ## 1.10.4 - 2017-01-20 569 | 570 | - `Activity` now extends from `Model` instead of `Eloquent` 571 | 572 | ## 1.10.2 - 2016-11-26 573 | 574 | - fix compatibilty for Laravel 5.1 575 | 576 | ## 1.10.1 - 2016-10-11 577 | 578 | - fix `scopeCausedBy` and `scopeForSubject` 579 | 580 | ## 1.10.0 - 2016-10-10 581 | 582 | - add support for `restored` event 583 | 584 | ## 1.9.2 - 2016-09-27 585 | 586 | - fixed a bug where the delete event would not be logged 587 | 588 | ## 1.9.1 - 2016-09-16 589 | 590 | - fixed the return value of `activity()->log()`. It will now return the created `Activity`-model. 591 | 592 | ## 1.9.0 - 2016-09-16 593 | 594 | - added `Macroable` to `ActivityLogger` 595 | 596 | ## 1.8.0 - 2016-09-12 597 | 598 | - added `causedBy` and `forSubject` scopes 599 | 600 | ## 1.7.1 - 2016-08-23 601 | 602 | - Added L5.3 compatibility 603 | 604 | ## 1.7.0 - 2016-08-17 605 | 606 | - Added `enabled` option in the config file. 607 | 608 | ## 1.6.0 - 2016-08-11 609 | 610 | - Added `ignoreChangedAttributes` 611 | 612 | ## 1.5.0 - 2016-08-11 613 | 614 | - Added support for using a custom `Activity` model 615 | 616 | ## 1.4.0 - 2016-08-10 617 | 618 | - Added support for soft deletes 619 | 620 | ## 1.3.2 - 2016-08-09 621 | 622 | - This version replaces version `1.3.0` 623 | - Dropped L5.1 compatibility 624 | 625 | ## 1.3.1 - 2016-08-09 626 | 627 | - this version removes the features introduced in 1.3.0 and is compatible with L5.1 628 | 629 | ## 1.3.0 - 2016-07-29 630 | 631 | **DO NOT USE THIS VERSION IF YOU'RE ON L5.1** 632 | 633 | Please upgrade to: 634 | 635 | - `1.3.1` for Laravel 5.1 636 | - `1.3.2` for Laravel 5.2 and higher 637 | 638 | Introduced features 639 | 640 | - made the auth driver configurable 641 | 642 | ## 1.3.0 - 2016-07-29 643 | 644 | - made the auth driver configurable 645 | 646 | ## 1.2.1 - 2016-07-09 647 | 648 | - use config repo contract 649 | 650 | ## 1.2.0 - 2016-07-08 651 | 652 | - added `getLogNameToUse` 653 | 654 | ## 1.1.0 - 2016-07-04 655 | 656 | - added `activity`-method on both the `CausesActivity` and `LogsActivity`-trait 657 | 658 | ## 1.0.3 - 2016-07-01 659 | 660 | - the package is now compatible with Laravel 5.1 661 | 662 | ## 1.0.2 - 2016-06-29 663 | 664 | - fixed naming of `inLog` scope 665 | - add `inLog` function alias 666 | 667 | ## 1.0.1 - 2016-06-29 668 | 669 | - fixed error when publishing migrations 670 | 671 | ## 1.0.0 - 2016-06-28 672 | 673 | - initial release 674 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Spatie bvba 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | Logo for laravel-activitylog 6 | 7 | 8 | 9 |

Log activity inside your Laravel app

10 | 11 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/laravel-activitylog.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-activitylog) 12 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spatie/laravel-activitylog/run-tests.yml?branch=main&label=Tests)](https://github.com/spatie/laravel-activitylog/actions/workflows/run-tests.yml) 13 | [![Check & fix styling](https://github.com/spatie/laravel-activitylog/workflows/Check%20&%20fix%20styling/badge.svg)](https://github.com/spatie/laravel-activitylog/actions/workflows/php-cs-fixer.yml) 14 | [![Total Downloads](https://img.shields.io/packagist/dt/spatie/laravel-activitylog.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-activitylog) 15 | 16 |
17 | 18 | The `spatie/laravel-activitylog` package provides easy to use functions to log the activities of the users of your app. It can also automatically log model events. 19 | The Package stores all activity in the `activity_log` table. 20 | 21 | Here's a demo of how you can use it: 22 | 23 | ```php 24 | activity()->log('Look, I logged something'); 25 | ``` 26 | 27 | You can retrieve all activity using the `Spatie\Activitylog\Models\Activity` model. 28 | 29 | ```php 30 | Activity::all(); 31 | ``` 32 | 33 | Here's a more advanced example: 34 | 35 | ```php 36 | activity() 37 | ->performedOn($anEloquentModel) 38 | ->causedBy($user) 39 | ->withProperties(['customProperty' => 'customValue']) 40 | ->log('Look, I logged something'); 41 | 42 | $lastLoggedActivity = Activity::all()->last(); 43 | 44 | $lastLoggedActivity->subject; //returns an instance of an eloquent model 45 | $lastLoggedActivity->causer; //returns an instance of your user model 46 | $lastLoggedActivity->getExtraProperty('customProperty'); //returns 'customValue' 47 | $lastLoggedActivity->description; //returns 'Look, I logged something' 48 | ``` 49 | 50 | Here's an example on [event logging](https://spatie.be/docs/laravel-activitylog/advanced-usage/logging-model-events). 51 | 52 | ```php 53 | $newsItem->name = 'updated name'; 54 | $newsItem->save(); 55 | 56 | //updating the newsItem will cause the logging of an activity 57 | $activity = Activity::all()->last(); 58 | 59 | $activity->description; //returns 'updated' 60 | $activity->subject; //returns the instance of NewsItem that was saved 61 | ``` 62 | 63 | Calling `$activity->changes()` will return this array: 64 | 65 | ```php 66 | [ 67 | 'attributes' => [ 68 | 'name' => 'updated name', 69 | 'text' => 'Lorum', 70 | ], 71 | 'old' => [ 72 | 'name' => 'original name', 73 | 'text' => 'Lorum', 74 | ], 75 | ]; 76 | ``` 77 | 78 | ## Support us 79 | 80 | [](https://spatie.be/github-ad-click/laravel-activitylog) 81 | 82 | We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us). 83 | 84 | We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards). 85 | 86 | ## Documentation 87 | 88 | You'll find the documentation on [https://spatie.be/docs/laravel-activitylog/introduction](https://spatie.be/docs/laravel-activitylog/introduction). 89 | 90 | Find yourself stuck using the package? Found a bug? Do you have general questions or suggestions for improving the activity log? Feel free to [create an issue on GitHub](https://github.com/spatie/laravel-activitylog/issues), we'll try to address it as soon as possible. 91 | 92 | ## Installation 93 | 94 | You can install the package via composer: 95 | 96 | ```bash 97 | composer require spatie/laravel-activitylog 98 | ``` 99 | 100 | The package will automatically register itself. 101 | 102 | You can publish the migration with: 103 | 104 | ```bash 105 | php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-migrations" 106 | ``` 107 | 108 | _Note_: The default migration assumes you are using integers for your model IDs. If you are using UUIDs, or some other format, adjust the format of the `subject_id` and `causer_id` fields in the published migration before continuing. 109 | 110 | After publishing the migration you can create the `activity_log` table by running the migrations: 111 | 112 | ```bash 113 | php artisan migrate 114 | ``` 115 | 116 | You can optionally publish the config file with: 117 | 118 | ```bash 119 | php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-config" 120 | ``` 121 | 122 | ## Changelog 123 | 124 | Please see [CHANGELOG](CHANGELOG.md) for more information about recent changes. 125 | 126 | ## Upgrading 127 | 128 | Please see [UPGRADING](UPGRADING.md) for details. 129 | 130 | ## Testing 131 | 132 | ```bash 133 | composer test 134 | ``` 135 | 136 | ## Contributing 137 | 138 | Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details. 139 | 140 | ## Security 141 | 142 | If you've found a bug regarding security please mail [security@spatie.be](mailto:security@spatie.be) instead of using the issue tracker. 143 | 144 | ## Credits 145 | 146 | - [Freek Van der Herten](https://github.com/freekmurze) 147 | - [Sebastian De Deyne](https://github.com/sebastiandedeyne) 148 | - [Tom Witkowski](https://github.com/Gummibeer) 149 | - [All Contributors](../../contributors) 150 | 151 | Special thanks to [Ahmed Nagi](https://github.com/nagi1) for all the work he put in `v4` and to [Caneco](https://twitter.com/caneco) for the original logo. 152 | 153 | ## License 154 | 155 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 156 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | ## From v3 to v4 2 | 3 | ``` bash 4 | composer require spatie/laravel-activitylog "^4.0.0" 5 | ``` 6 | 7 | ### Publish migrations & migrate new tables 8 | 9 | ``` bash 10 | php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-migrations" 11 | php artisan migrate 12 | ``` 13 | 14 | ### Model event Logging 15 | 16 | - All models now need to define a `getActivitylogOptions()` method to configure and return the models options as a `LogOptions` instance. 17 | - To control what attributes are logged, instead of defining a `$logAttributes` property this is defined in the `getActivitylogOptions()` method using the `logOnly()` method of `LogOptions`. 18 | - The `getDescriptionForEvent()` method is no longer used to customize the description. Instead, use the `setDescriptionForEvent()` method for `LogOptions` class. 19 | - When customizing the log's name instead of defining a `$logName` property, call the `useLogName()` method when configuring the `LogOptions`. 20 | - Instead of the `$ignoreChangedAttributes` property the ` dontLogIfAttributesChangedOnly()` method should be used. 21 | - If you only need to log the dirty attributes use `logOnlyDirty()` since the `$logOnlyDirty` property is no longer used. 22 | - For instances where you do not want to store empty log events use `dontSubmitEmptyLogs()` instead of setting `$submitEmptyLogs` to `false`. 23 | - When you use a `*` (wildcard) and want to ignore specific elements use the `dontLogIfAttributesChangedOnly()` method instead of the `$logAttributesToIgnore` property. 24 | 25 | ## From v2 to v3 26 | 27 | - if you are using a custom `Activity` model, you should let it implement the new `Spatie\Activitylog\Contracts\Activity` interface 28 | - the preferred way to get changes on an `Activity` model is through the `changes` property instead of the `changes()` function. Change all usages from 29 | `$activity->changes()` to `$activity->changes` 30 | - the `activity` relation of the `CausesActivity` trait has been renamed to `actions`. Rename all uses from `$user->activity` to `$user->actions` 31 | - the `activity` relation of the `LogsActivity` trait has been renamed to `activities`. Rename all uses from `$yourModel->activity` to `$yourModel->activities`. 32 | - the deprecated `loggedActivity` relation has been removed. Use `activities` instead. 33 | - the `HasActivity` trait has been removed. Use both `CausesActivity` and `LogsActivity` traits instead. 34 | -------------------------------------------------------------------------------- /art/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Laravel Activity Log Art 6 | 7 | The logo was inspired by the [Spatie](https://spatie.be) brand, and the well known minimal design of Laravel packages. 8 | 9 | ## Fonts 10 | 11 | The logo is using the following fonts: 12 | 13 | - [Inter 500](https://fonts.google.com/specimen/Inter#500) 14 | - [Inter 600](https://fonts.google.com/specimen/Inter#600) 15 | 16 | ## Colors 17 | 18 | | |#hex |rgb() | 19 | |--- |--- |--- | 20 | |![100](/art/palette/100.png)|`#E8F1F4`|`rgb(232,241,244)`| 21 | |![200](/art/palette/200.png)|`#C6DDE4`|`rgb(198,221,228)`| 22 | |![300](/art/palette/300.png)|`#A3C8D4`|`rgb(163,200,212)`| 23 | |![400](/art/palette/400.png)|`#5E9EB3`|`rgb(94,158,179)` | 24 | |![500](/art/palette/500.png)|`#197593`|`rgb(25,117,147)` | 25 | |![600](/art/palette/600.png)|`#176984`|`rgb(23,105,132)` | 26 | |![700](/art/palette/700.png)|`#0F4658`|`rgb(15,70,88)` | 27 | |![800](/art/palette/800.png)|`#0B3542`|`rgb(11,53,66)` | 28 | |![900](/art/palette/900.png)|`#08232C`|`rgb(8,35,44)` | 29 | 30 | ## Requirements 31 | 32 | - A screen or a printer 33 | 34 | ## Installation 35 | 36 | - Open the file 37 | - *Right-click* on the image 38 | - Choose **"Save image as…"** option 39 | 40 | ## Maintainers 41 | 42 | **Laravel Activity Log** logo is designed and maintained by [Caneco](https://twitter.com/caneco). 43 | 44 | ## License 45 | 46 | All rights reserved, but with the following extra conditions: 47 | 48 | - It is **OK** to use the Laravel Activity Log logo in the following cases: 49 | - In marketing materials for technical events, e.g. meetups, hackathons, conferences and workshops that are related to Laravel. 50 | - In open source projects related to Laravel. 51 | - In technical articles/videos/books/papers for educational purposes. 52 | - To illustrate a commercial product. 53 | 54 | - It is **NOT OK** to use the Laravel Activity Log logo in the following cases without prior written consent from the copyright owners: 55 | - Using the Laravel Activity Log logo in a commercial product for purposes other than illustrating its integration. 56 | - Sell physical products that uses the Laravel Activity Log logo or its variants, e.g. t-shirts. 57 | 58 | By any means the owner reserves the right of final explanation for any use case not explicitly stated above. 59 | -------------------------------------------------------------------------------- /art/logomark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/logomark.png -------------------------------------------------------------------------------- /art/logomark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /art/logomark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/logomark@2x.png -------------------------------------------------------------------------------- /art/logomark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/logomark@3x.png -------------------------------------------------------------------------------- /art/logomark@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/logomark@4x.png -------------------------------------------------------------------------------- /art/palette/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/palette/100.png -------------------------------------------------------------------------------- /art/palette/200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/palette/200.png -------------------------------------------------------------------------------- /art/palette/300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/palette/300.png -------------------------------------------------------------------------------- /art/palette/400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/palette/400.png -------------------------------------------------------------------------------- /art/palette/500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/palette/500.png -------------------------------------------------------------------------------- /art/palette/600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/palette/600.png -------------------------------------------------------------------------------- /art/palette/700.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/palette/700.png -------------------------------------------------------------------------------- /art/palette/800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/palette/800.png -------------------------------------------------------------------------------- /art/palette/900.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/palette/900.png -------------------------------------------------------------------------------- /art/socialcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spatie/laravel-activitylog/aba0f98af47e58362fad11833a194ab461542c74/art/socialcard.png -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spatie/laravel-activitylog", 3 | "description": "A very simple activity logger to monitor the users of your website or application", 4 | "license": "MIT", 5 | "keywords": [ 6 | "spatie", 7 | "log", 8 | "user", 9 | "activity", 10 | "laravel" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Freek Van der Herten", 15 | "email": "freek@spatie.be", 16 | "homepage": "https://spatie.be", 17 | "role": "Developer" 18 | }, 19 | { 20 | "name": "Sebastian De Deyne", 21 | "email": "sebastian@spatie.be", 22 | "homepage": "https://spatie.be", 23 | "role": "Developer" 24 | }, 25 | { 26 | "name": "Tom Witkowski", 27 | "email": "dev.gummibeer@gmail.com", 28 | "homepage": "https://gummibeer.de", 29 | "role": "Developer" 30 | } 31 | ], 32 | "homepage": "https://github.com/spatie/activitylog", 33 | "require": { 34 | "php": "^8.1", 35 | "illuminate/config": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", 36 | "illuminate/database": "^8.69 || ^9.27 || ^10.0 || ^11.0 || ^12.0", 37 | "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", 38 | "spatie/laravel-package-tools": "^1.6.3" 39 | }, 40 | "require-dev": { 41 | "ext-json": "*", 42 | "orchestra/testbench": "^6.23 || ^7.0 || ^8.0 || ^9.0 || ^10.0", 43 | "pestphp/pest": "^1.20 || ^2.0 || ^3.0" 44 | }, 45 | "minimum-stability": "dev", 46 | "prefer-stable": true, 47 | "autoload": { 48 | "psr-4": { 49 | "Spatie\\Activitylog\\": "src" 50 | }, 51 | "files": [ 52 | "src/helpers.php" 53 | ] 54 | }, 55 | "autoload-dev": { 56 | "psr-4": { 57 | "Spatie\\Activitylog\\Test\\": "tests" 58 | } 59 | }, 60 | "config": { 61 | "allow-plugins": { 62 | "pestphp/pest-plugin": true 63 | }, 64 | "sort-packages": true 65 | }, 66 | "extra": { 67 | "laravel": { 68 | "providers": [ 69 | "Spatie\\Activitylog\\ActivitylogServiceProvider" 70 | ] 71 | } 72 | }, 73 | "scripts": { 74 | "test": "vendor/bin/pest" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /config/activitylog.php: -------------------------------------------------------------------------------- 1 | env('ACTIVITY_LOGGER_ENABLED', true), 9 | 10 | /* 11 | * When the clean-command is executed, all recording activities older than 12 | * the number of days specified here will be deleted. 13 | */ 14 | 'delete_records_older_than_days' => 365, 15 | 16 | /* 17 | * If no log name is passed to the activity() helper 18 | * we use this default log name. 19 | */ 20 | 'default_log_name' => 'default', 21 | 22 | /* 23 | * You can specify an auth driver here that gets user models. 24 | * If this is null we'll use the current Laravel auth driver. 25 | */ 26 | 'default_auth_driver' => null, 27 | 28 | /* 29 | * If set to true, the subject returns soft deleted models. 30 | */ 31 | 'subject_returns_soft_deleted_models' => false, 32 | 33 | /* 34 | * This model will be used to log activity. 35 | * It should implement the Spatie\Activitylog\Contracts\Activity interface 36 | * and extend Illuminate\Database\Eloquent\Model. 37 | */ 38 | 'activity_model' => \Spatie\Activitylog\Models\Activity::class, 39 | 40 | /* 41 | * This is the name of the table that will be created by the migration and 42 | * used by the Activity model shipped with this package. 43 | */ 44 | 'table_name' => env('ACTIVITY_LOGGER_TABLE_NAME', 'activity_log'), 45 | 46 | /* 47 | * This is the database connection that will be used by the migration and 48 | * the Activity model shipped with this package. In case it's not set 49 | * Laravel's database.default will be used instead. 50 | */ 51 | 'database_connection' => env('ACTIVITY_LOGGER_DB_CONNECTION'), 52 | ]; 53 | -------------------------------------------------------------------------------- /database/migrations/add_batch_uuid_column_to_activity_log_table.php.stub: -------------------------------------------------------------------------------- 1 | table(config('activitylog.table_name'), function (Blueprint $table) { 12 | $table->uuid('batch_uuid')->nullable()->after('properties'); 13 | }); 14 | } 15 | 16 | public function down() 17 | { 18 | Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) { 19 | $table->dropColumn('batch_uuid'); 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /database/migrations/add_event_column_to_activity_log_table.php.stub: -------------------------------------------------------------------------------- 1 | table(config('activitylog.table_name'), function (Blueprint $table) { 12 | $table->string('event')->nullable()->after('subject_type'); 13 | }); 14 | } 15 | 16 | public function down() 17 | { 18 | Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) { 19 | $table->dropColumn('event'); 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /database/migrations/create_activity_log_table.php.stub: -------------------------------------------------------------------------------- 1 | create(config('activitylog.table_name'), function (Blueprint $table) { 12 | $table->bigIncrements('id'); 13 | $table->string('log_name')->nullable(); 14 | $table->text('description'); 15 | $table->nullableMorphs('subject', 'subject'); 16 | $table->nullableMorphs('causer', 'causer'); 17 | $table->json('properties')->nullable(); 18 | $table->timestamps(); 19 | $table->index('log_name'); 20 | }); 21 | } 22 | 23 | public function down() 24 | { 25 | Schema::connection(config('activitylog.database_connection'))->dropIfExists(config('activitylog.table_name')); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ActivityLogStatus.php: -------------------------------------------------------------------------------- 1 | enabled = $config['activitylog.enabled']; 14 | } 15 | 16 | public function enable(): bool 17 | { 18 | return $this->enabled = true; 19 | } 20 | 21 | public function disable(): bool 22 | { 23 | return $this->enabled = false; 24 | } 25 | 26 | public function disabled(): bool 27 | { 28 | return $this->enabled === false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ActivityLogger.php: -------------------------------------------------------------------------------- 1 | causerResolver = $causerResolver; 33 | 34 | $this->batch = $batch; 35 | 36 | $this->defaultLogName = $config['activitylog']['default_log_name']; 37 | 38 | $this->logStatus = $logStatus; 39 | } 40 | 41 | public function setLogStatus(ActivityLogStatus $logStatus): static 42 | { 43 | $this->logStatus = $logStatus; 44 | 45 | return $this; 46 | } 47 | 48 | public function performedOn(Model $model): static 49 | { 50 | $this->getActivity()->subject()->associate($model); 51 | 52 | return $this; 53 | } 54 | 55 | public function on(Model $model): static 56 | { 57 | return $this->performedOn($model); 58 | } 59 | 60 | public function causedBy(Model | int | string | null $modelOrId): static 61 | { 62 | if ($modelOrId === null) { 63 | return $this; 64 | } 65 | 66 | $model = $this->causerResolver->resolve($modelOrId); 67 | 68 | $this->getActivity()->causer()->associate($model); 69 | 70 | return $this; 71 | } 72 | 73 | public function by(Model | int | string | null $modelOrId): static 74 | { 75 | return $this->causedBy($modelOrId); 76 | } 77 | 78 | public function causedByAnonymous(): static 79 | { 80 | $this->activity->causer_id = null; 81 | $this->activity->causer_type = null; 82 | 83 | return $this; 84 | } 85 | 86 | public function byAnonymous(): static 87 | { 88 | return $this->causedByAnonymous(); 89 | } 90 | 91 | public function event(string $event): static 92 | { 93 | return $this->setEvent($event); 94 | } 95 | 96 | public function setEvent(string $event): static 97 | { 98 | $this->activity->event = $event; 99 | 100 | return $this; 101 | } 102 | 103 | public function withProperties(mixed $properties): static 104 | { 105 | $this->getActivity()->properties = collect($properties); 106 | 107 | return $this; 108 | } 109 | 110 | public function withProperty(string $key, mixed $value): static 111 | { 112 | $this->getActivity()->properties = $this->getActivity()->properties->put($key, $value); 113 | 114 | return $this; 115 | } 116 | 117 | public function createdAt(DateTimeInterface $dateTime): static 118 | { 119 | $this->getActivity()->created_at = Carbon::instance($dateTime); 120 | 121 | return $this; 122 | } 123 | 124 | public function useLog(?string $logName): static 125 | { 126 | $this->getActivity()->log_name = $logName; 127 | 128 | return $this; 129 | } 130 | 131 | public function inLog(?string $logName): static 132 | { 133 | return $this->useLog($logName); 134 | } 135 | 136 | public function tap(callable $callback, ?string $eventName = null): static 137 | { 138 | call_user_func($callback, $this->getActivity(), $eventName); 139 | 140 | return $this; 141 | } 142 | 143 | public function enableLogging(): static 144 | { 145 | $this->logStatus->enable(); 146 | 147 | return $this; 148 | } 149 | 150 | public function disableLogging(): static 151 | { 152 | $this->logStatus->disable(); 153 | 154 | return $this; 155 | } 156 | 157 | public function log(string $description): ?ActivityContract 158 | { 159 | if ($this->logStatus->disabled()) { 160 | return null; 161 | } 162 | 163 | $activity = $this->activity; 164 | 165 | $activity->description = $this->replacePlaceholders( 166 | $activity->description ?? $description, 167 | $activity 168 | ); 169 | 170 | if (isset($activity->subject) && method_exists($activity->subject, 'tapActivity')) { 171 | $this->tap([$activity->subject, 'tapActivity'], $activity->event ?? ''); 172 | } 173 | 174 | $activity->save(); 175 | 176 | $this->activity = null; 177 | 178 | return $activity; 179 | } 180 | 181 | public function withoutLogs(Closure $callback): mixed 182 | { 183 | if ($this->logStatus->disabled()) { 184 | return $callback(); 185 | } 186 | 187 | $this->logStatus->disable(); 188 | 189 | try { 190 | return $callback(); 191 | } finally { 192 | $this->logStatus->enable(); 193 | } 194 | } 195 | 196 | protected function replacePlaceholders(string $description, ActivityContract $activity): string 197 | { 198 | return preg_replace_callback('/:[a-z0-9._-]+(?$attribute; 210 | 211 | if (is_null($attributeValue)) { 212 | return $match; 213 | } 214 | 215 | return data_get($attributeValue, $propertyName, $match); 216 | }, $description); 217 | } 218 | 219 | protected function getActivity(): ActivityContract 220 | { 221 | if (! $this->activity instanceof ActivityContract) { 222 | $this->activity = ActivitylogServiceProvider::getActivityModelInstance(); 223 | $this 224 | ->useLog($this->defaultLogName) 225 | ->withProperties([]) 226 | ->causedBy($this->causerResolver->resolve()); 227 | 228 | $this->activity->batch_uuid = $this->batch->getUuid(); 229 | } 230 | 231 | return $this->activity; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/ActivitylogServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('laravel-activitylog') 19 | ->hasConfigFile('activitylog') 20 | ->hasMigrations([ 21 | 'create_activity_log_table', 22 | 'add_event_column_to_activity_log_table', 23 | 'add_batch_uuid_column_to_activity_log_table', 24 | ]) 25 | ->hasCommand(CleanActivitylogCommand::class); 26 | } 27 | 28 | public function registeringPackage() 29 | { 30 | $this->app->bind(ActivityLogger::class); 31 | 32 | $this->app->scoped(LogBatch::class); 33 | 34 | $this->app->scoped(CauserResolver::class); 35 | 36 | $this->app->scoped(ActivityLogStatus::class); 37 | } 38 | 39 | public static function determineActivityModel(): string 40 | { 41 | $activityModel = config('activitylog.activity_model') ?? ActivityModel::class; 42 | 43 | if (! is_a($activityModel, Activity::class, true) 44 | || ! is_a($activityModel, Model::class, true)) { 45 | throw InvalidConfiguration::modelIsNotValid($activityModel); 46 | } 47 | 48 | return $activityModel; 49 | } 50 | 51 | public static function getActivityModelInstance(): ActivityContract 52 | { 53 | $activityModelClassName = self::determineActivityModel(); 54 | 55 | return new $activityModelClassName(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/CauserResolver.php: -------------------------------------------------------------------------------- 1 | authManager = $authManager; 24 | 25 | $this->authDriver = $config['activitylog']['default_auth_driver']; 26 | } 27 | 28 | public function resolve(Model | int | string | null $subject = null): ?Model 29 | { 30 | if ($this->causerOverride !== null) { 31 | return $this->causerOverride; 32 | } 33 | 34 | if ($this->resolverOverride !== null) { 35 | $resultCauser = ($this->resolverOverride)($subject); 36 | 37 | if (! $this->isResolvable($resultCauser)) { 38 | throw CouldNotLogActivity::couldNotDetermineUser($resultCauser); 39 | } 40 | 41 | return $resultCauser; 42 | } 43 | 44 | return $this->getCauser($subject); 45 | } 46 | 47 | protected function resolveUsingId(int | string $subject): Model 48 | { 49 | $guard = $this->authManager->guard($this->authDriver); 50 | 51 | $provider = method_exists($guard, 'getProvider') ? $guard->getProvider() : null; 52 | $model = method_exists($provider, 'retrieveById') ? $provider->retrieveById($subject) : null; 53 | 54 | throw_unless($model instanceof Model, CouldNotLogActivity::couldNotDetermineUser($subject)); 55 | 56 | return $model; 57 | } 58 | 59 | protected function getCauser(Model | int | string | null $subject = null): ?Model 60 | { 61 | if ($subject instanceof Model) { 62 | return $subject; 63 | } 64 | 65 | if (is_null($subject)) { 66 | return $this->getDefaultCauser(); 67 | } 68 | 69 | return $this->resolveUsingId($subject); 70 | } 71 | 72 | /** 73 | * Override the resover using callback. 74 | */ 75 | public function resolveUsing(Closure $callback): static 76 | { 77 | $this->resolverOverride = $callback; 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Override default causer. 84 | */ 85 | public function setCauser(?Model $causer): static 86 | { 87 | $this->causerOverride = $causer; 88 | 89 | return $this; 90 | } 91 | 92 | protected function isResolvable(mixed $model): bool 93 | { 94 | return $model instanceof Model || is_null($model); 95 | } 96 | 97 | protected function getDefaultCauser(): ?Model 98 | { 99 | return $this->authManager->guard($this->authDriver)->user(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/CleanActivitylogCommand.php: -------------------------------------------------------------------------------- 1 | confirmToProceed()) { 24 | return 1; 25 | } 26 | 27 | $this->comment('Cleaning activity log...'); 28 | 29 | $log = $this->argument('log'); 30 | 31 | $maxAgeInDays = $this->option('days') ?? config('activitylog.delete_records_older_than_days'); 32 | 33 | $cutOffDate = Carbon::now()->subDays($maxAgeInDays)->format('Y-m-d H:i:s'); 34 | 35 | $activity = ActivitylogServiceProvider::getActivityModelInstance(); 36 | 37 | $amountDeleted = $activity::query() 38 | ->where('created_at', '<', $cutOffDate) 39 | ->when($log !== null, function (Builder $query) use ($log) { 40 | $query->inLog($log); 41 | }) 42 | ->delete(); 43 | 44 | $this->info("Deleted {$amountDeleted} record(s) from the activity log."); 45 | 46 | $this->comment('All done!'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Contracts/Activity.php: -------------------------------------------------------------------------------- 1 | options ??= $model->getActivitylogOptions(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/CouldNotLogActivity.php: -------------------------------------------------------------------------------- 1 | toString(); 17 | } 18 | 19 | public function getUuid(): ?string 20 | { 21 | return $this->uuid; 22 | } 23 | 24 | public function setBatch(string $uuid): void 25 | { 26 | $this->uuid = $uuid; 27 | $this->transactions = 1; 28 | } 29 | 30 | public function withinBatch(Closure $callback): mixed 31 | { 32 | $this->startBatch(); 33 | $result = $callback($this->getUuid()); 34 | $this->endBatch(); 35 | 36 | return $result; 37 | } 38 | 39 | public function startBatch(): void 40 | { 41 | if (! $this->isOpen()) { 42 | $this->uuid = $this->generateUuid(); 43 | } 44 | 45 | $this->transactions++; 46 | } 47 | 48 | public function isOpen(): bool 49 | { 50 | return $this->transactions > 0; 51 | } 52 | 53 | public function endBatch(): void 54 | { 55 | $this->transactions = max(0, $this->transactions - 1); 56 | 57 | if ($this->transactions === 0) { 58 | $this->uuid = null; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/LogOptions.php: -------------------------------------------------------------------------------- 1 | logOnly(['*']); 43 | } 44 | 45 | /** 46 | * Log all attributes that are not listed in $guarded. 47 | */ 48 | public function logUnguarded(): self 49 | { 50 | $this->logUnguarded = true; 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * log changes to all the $fillable attributes of the model. 57 | */ 58 | public function logFillable(): self 59 | { 60 | $this->logFillable = true; 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * Stop logging $fillable attributes of the model. 67 | */ 68 | public function dontLogFillable(): self 69 | { 70 | $this->logFillable = false; 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Log changes that has actually changed after the update. 77 | */ 78 | public function logOnlyDirty(): self 79 | { 80 | $this->logOnlyDirty = true; 81 | 82 | return $this; 83 | } 84 | 85 | /** 86 | * Log changes only if these attributes changed. 87 | */ 88 | public function logOnly(array $attributes): self 89 | { 90 | $this->logAttributes = $attributes; 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * Exclude these attributes from being logged. 97 | */ 98 | public function logExcept(array $attributes): self 99 | { 100 | $this->logExceptAttributes = $attributes; 101 | 102 | return $this; 103 | } 104 | 105 | /** 106 | * Don't trigger an activity if these attributes changed logged. 107 | */ 108 | public function dontLogIfAttributesChangedOnly(array $attributes): self 109 | { 110 | $this->dontLogIfAttributesChangedOnly = $attributes; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Don't store empty logs. Storing empty logs can happen when you only 117 | * want to log a certain attribute but only another changes. 118 | */ 119 | public function dontSubmitEmptyLogs(): self 120 | { 121 | $this->submitEmptyLogs = false; 122 | 123 | return $this; 124 | } 125 | 126 | /** 127 | * Allow storing empty logs. Storing empty logs can happen when you only 128 | * want to log a certain attribute but only another changes. 129 | */ 130 | public function submitEmptyLogs(): self 131 | { 132 | $this->submitEmptyLogs = true; 133 | 134 | return $this; 135 | } 136 | 137 | /** 138 | * Customize log name. 139 | */ 140 | public function useLogName(?string $logName): self 141 | { 142 | $this->logName = $logName; 143 | 144 | return $this; 145 | } 146 | 147 | /** 148 | * Customize log description using callback. 149 | */ 150 | public function setDescriptionForEvent(Closure $callback): self 151 | { 152 | $this->descriptionForEvent = $callback; 153 | 154 | return $this; 155 | } 156 | 157 | /** 158 | * Exclude these attributes from being casted. 159 | */ 160 | public function useAttributeRawValues(array $attributes): self 161 | { 162 | $this->attributeRawValues = $attributes; 163 | 164 | return $this; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/Models/Activity.php: -------------------------------------------------------------------------------- 1 | 'collection', 47 | ]; 48 | 49 | public function __construct(array $attributes = []) 50 | { 51 | if (! isset($this->connection)) { 52 | $this->setConnection(config('activitylog.database_connection')); 53 | } 54 | 55 | if (! isset($this->table)) { 56 | $this->setTable(config('activitylog.table_name')); 57 | } 58 | 59 | parent::__construct($attributes); 60 | } 61 | 62 | public function subject(): MorphTo 63 | { 64 | if (config('activitylog.subject_returns_soft_deleted_models')) { 65 | return $this->morphTo()->withTrashed(); 66 | } 67 | 68 | return $this->morphTo(); 69 | } 70 | 71 | public function causer(): MorphTo 72 | { 73 | return $this->morphTo(); 74 | } 75 | 76 | public function getExtraProperty(string $propertyName, mixed $defaultValue = null): mixed 77 | { 78 | return Arr::get($this->properties->toArray(), $propertyName, $defaultValue); 79 | } 80 | 81 | public function changes(): Collection 82 | { 83 | if (! $this->properties instanceof Collection) { 84 | return new Collection(); 85 | } 86 | 87 | return $this->properties->only(['attributes', 'old']); 88 | } 89 | 90 | public function getChangesAttribute(): Collection 91 | { 92 | return $this->changes(); 93 | } 94 | 95 | public function scopeInLog(Builder $query, ...$logNames): Builder 96 | { 97 | if (is_array($logNames[0])) { 98 | $logNames = $logNames[0]; 99 | } 100 | 101 | return $query->whereIn('log_name', $logNames); 102 | } 103 | 104 | public function scopeCausedBy(Builder $query, Model $causer): Builder 105 | { 106 | return $query 107 | ->where('causer_type', $causer->getMorphClass()) 108 | ->where('causer_id', $causer->getKey()); 109 | } 110 | 111 | public function scopeForSubject(Builder $query, Model $subject): Builder 112 | { 113 | return $query 114 | ->where('subject_type', $subject->getMorphClass()) 115 | ->where('subject_id', $subject->getKey()); 116 | } 117 | 118 | public function scopeForEvent(Builder $query, string $event): Builder 119 | { 120 | return $query->where('event', $event); 121 | } 122 | 123 | public function scopeHasBatch(Builder $query): Builder 124 | { 125 | return $query->whereNotNull('batch_uuid'); 126 | } 127 | 128 | public function scopeForBatch(Builder $query, string $batchUuid): Builder 129 | { 130 | return $query->where('batch_uuid', $batchUuid); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/PendingActivityLog.php: -------------------------------------------------------------------------------- 1 | logger = $logger 19 | ->setLogStatus($status) 20 | ->useLog(config('activitylog.default_log_name')); 21 | } 22 | 23 | public function logger(): ActivityLogger 24 | { 25 | return $this->logger; 26 | } 27 | 28 | public function __call(string $method, array $parameters): mixed 29 | { 30 | return $this->forwardCallTo($this->logger, $method, $parameters); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Traits/CausesActivity.php: -------------------------------------------------------------------------------- 1 | morphMany( 13 | ActivitylogServiceProvider::determineActivityModel(), 14 | 'causer' 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Traits/LogsActivity.php: -------------------------------------------------------------------------------- 1 | each(function ($eventName) { 40 | if ($eventName === 'updated') { 41 | static::updating(function (Model $model) { 42 | $oldValues = (new static())->setRawAttributes($model->getRawOriginal()); 43 | $model->oldAttributes = static::logChanges($oldValues); 44 | }); 45 | } 46 | 47 | static::$eventName(function (Model $model) use ($eventName) { 48 | $model->activitylogOptions = $model->getActivitylogOptions(); 49 | 50 | if (! $model->shouldLogEvent($eventName)) { 51 | return; 52 | } 53 | 54 | $changes = $model->attributeValuesToBeLogged($eventName); 55 | 56 | $description = $model->getDescriptionForEvent($eventName); 57 | 58 | $logName = $model->getLogNameToUse(); 59 | 60 | // Submitting empty description will cause place holder replacer to fail. 61 | if ($description == '') { 62 | return; 63 | } 64 | 65 | if ($model->isLogEmpty($changes) && ! $model->activitylogOptions->submitEmptyLogs) { 66 | return; 67 | } 68 | 69 | // User can define a custom pipelines to mutate, add or remove from changes 70 | // each pipe receives the event carrier bag with changes and the model in 71 | // question every pipe should manipulate new and old attributes. 72 | $event = app(Pipeline::class) 73 | ->send(new EventLogBag($eventName, $model, $changes, $model->activitylogOptions)) 74 | ->through(static::$changesPipes) 75 | ->thenReturn(); 76 | 77 | // Actual logging 78 | $logger = app(ActivityLogger::class) 79 | ->useLog($logName) 80 | ->event($eventName) 81 | ->performedOn($model) 82 | ->withProperties($event->changes); 83 | 84 | if (method_exists($model, 'tapActivity')) { 85 | $logger->tap([$model, 'tapActivity'], $eventName); 86 | } 87 | 88 | $logger->log($description); 89 | 90 | // Reset log options so the model can be serialized. 91 | $model->activitylogOptions = null; 92 | }); 93 | }); 94 | } 95 | 96 | public static function addLogChange(LoggablePipe $pipe): void 97 | { 98 | static::$changesPipes[] = $pipe; 99 | } 100 | 101 | public function isLogEmpty(array $changes): bool 102 | { 103 | return empty($changes['attributes'] ?? []) && empty($changes['old'] ?? []); 104 | } 105 | 106 | public function disableLogging(): self 107 | { 108 | $this->enableLoggingModelsEvents = false; 109 | 110 | return $this; 111 | } 112 | 113 | public function enableLogging(): self 114 | { 115 | $this->enableLoggingModelsEvents = true; 116 | 117 | return $this; 118 | } 119 | 120 | public function activities(): MorphMany 121 | { 122 | return $this->morphMany(ActivitylogServiceProvider::determineActivityModel(), 'subject'); 123 | } 124 | 125 | public function getDescriptionForEvent(string $eventName): string 126 | { 127 | if (! empty($this->activitylogOptions->descriptionForEvent)) { 128 | return ($this->activitylogOptions->descriptionForEvent)($eventName); 129 | } 130 | 131 | return $eventName; 132 | } 133 | 134 | public function getLogNameToUse(): ?string 135 | { 136 | if (! empty($this->activitylogOptions->logName)) { 137 | return $this->activitylogOptions->logName; 138 | } 139 | 140 | return config('activitylog.default_log_name'); 141 | } 142 | 143 | /** 144 | * Get the event names that should be recorded. 145 | **/ 146 | protected static function eventsToBeRecorded(): Collection 147 | { 148 | if (isset(static::$recordEvents)) { 149 | return collect(static::$recordEvents); 150 | } 151 | 152 | $events = collect([ 153 | 'created', 154 | 'updated', 155 | 'deleted', 156 | ]); 157 | 158 | if (collect(class_uses_recursive(static::class))->contains(SoftDeletes::class)) { 159 | $events->push('restored'); 160 | } 161 | 162 | return $events; 163 | } 164 | 165 | protected function shouldLogEvent(string $eventName): bool 166 | { 167 | $logStatus = app(ActivityLogStatus::class); 168 | 169 | if (! $this->enableLoggingModelsEvents || $logStatus->disabled()) { 170 | return false; 171 | } 172 | 173 | if (! in_array($eventName, ['created', 'updated'])) { 174 | return true; 175 | } 176 | 177 | // Do not log update event if the model is restoring 178 | if ($this->isRestoring()) { 179 | return false; 180 | } 181 | 182 | // Do not log update event if only ignored attributes are changed. 183 | return (bool) count(Arr::except($this->getDirty(), $this->activitylogOptions->dontLogIfAttributesChangedOnly)); 184 | } 185 | 186 | /** 187 | * Determines if the model is restoring. 188 | **/ 189 | protected function isRestoring(): bool 190 | { 191 | $deletedAtColumn = method_exists($this, 'getDeletedAtColumn') 192 | ? $this->getDeletedAtColumn() 193 | : 'deleted_at'; 194 | 195 | return $this->isDirty($deletedAtColumn) && count($this->getDirty()) === 1; 196 | } 197 | 198 | /** 199 | * Determines what attributes needs to be logged based on the configuration. 200 | **/ 201 | public function attributesToBeLogged(): array 202 | { 203 | $this->activitylogOptions = $this->getActivitylogOptions(); 204 | 205 | $attributes = []; 206 | 207 | // Check if fillable attributes will be logged then merge it to the local attributes array. 208 | if ($this->activitylogOptions->logFillable) { 209 | $attributes = array_merge($attributes, $this->getFillable()); 210 | } 211 | 212 | // Determine if unguarded attributes will be logged. 213 | if ($this->shouldLogUnguarded()) { 214 | 215 | // Get only attribute names, not intrested in the values here then guarded 216 | // attributes. get only keys than not present in guarded array, because 217 | // we are logging the unguarded attributes and we cant have both! 218 | 219 | $attributes = array_merge($attributes, array_diff(array_keys($this->getAttributes()), $this->getGuarded())); 220 | } 221 | 222 | if (! empty($this->activitylogOptions->logAttributes)) { 223 | 224 | // Filter * from the logAttributes because will deal with it separately 225 | $attributes = array_merge($attributes, array_diff($this->activitylogOptions->logAttributes, ['*'])); 226 | 227 | // If there's * get all attributes then merge it, dont respect $guarded or $fillable. 228 | if (in_array('*', $this->activitylogOptions->logAttributes)) { 229 | $attributes = array_merge($attributes, array_keys($this->getAttributes())); 230 | } 231 | } 232 | 233 | if ($this->activitylogOptions->logExceptAttributes) { 234 | 235 | // Filter out the attributes defined in ignoredAttributes out of the local array 236 | $attributes = array_diff($attributes, $this->activitylogOptions->logExceptAttributes); 237 | } 238 | 239 | return $attributes; 240 | } 241 | 242 | public function shouldLogUnguarded(): bool 243 | { 244 | if (! $this->activitylogOptions->logUnguarded) { 245 | return false; 246 | } 247 | 248 | // This case means all of the attributes are guarded 249 | // so we'll not have any unguarded anyway. 250 | if (in_array('*', $this->getGuarded())) { 251 | return false; 252 | } 253 | 254 | return true; 255 | } 256 | 257 | /** 258 | * Determines values that will be logged based on the difference. 259 | **/ 260 | public function attributeValuesToBeLogged(string $processingEvent): array 261 | { 262 | // no loggable attributes, no values to be logged! 263 | if (! count($this->attributesToBeLogged())) { 264 | return []; 265 | } 266 | 267 | $properties['attributes'] = static::logChanges( 268 | 269 | // if the current event is retrieved, get the model itself 270 | // else get the fresh default properties from database 271 | // as wouldn't be part of the saved model instance. 272 | $processingEvent == 'retrieved' 273 | ? $this 274 | : ( 275 | $this->exists 276 | ? $this->fresh() ?? $this 277 | : $this 278 | ) 279 | ); 280 | 281 | if (static::eventsToBeRecorded()->contains('updated') && $processingEvent == 'updated') { 282 | 283 | // Fill the attributes with null values. 284 | $nullProperties = array_fill_keys(array_keys($properties['attributes']), null); 285 | 286 | // Populate the old key with keys from database and from old attributes. 287 | $properties['old'] = array_merge($nullProperties, $this->oldAttributes); 288 | 289 | // Fail safe. 290 | $this->oldAttributes = []; 291 | } 292 | 293 | if ($this->activitylogOptions->logOnlyDirty && isset($properties['old'])) { 294 | 295 | // Get difference between the old and new attributes. 296 | $properties['attributes'] = array_udiff_assoc( 297 | $properties['attributes'], 298 | $properties['old'], 299 | function ($new, $old) { 300 | // Strict check for php's weird behaviors 301 | if ($old === null || $new === null) { 302 | return $new === $old ? 0 : 1; 303 | } 304 | 305 | // Handles Date interval comparisons since php cannot use spaceship 306 | // Operator to compare them and will throw ErrorException. 307 | if ($old instanceof DateInterval) { 308 | return CarbonInterval::make($old)->equalTo($new) ? 0 : 1; 309 | } elseif ($new instanceof DateInterval) { 310 | return CarbonInterval::make($new)->equalTo($old) ? 0 : 1; 311 | } 312 | 313 | return $new <=> $old; 314 | } 315 | ); 316 | 317 | $properties['old'] = collect($properties['old']) 318 | ->only(array_keys($properties['attributes'])) 319 | ->all(); 320 | } 321 | 322 | if (static::eventsToBeRecorded()->contains('deleted') && $processingEvent == 'deleted') { 323 | $properties['old'] = $properties['attributes']; 324 | unset($properties['attributes']); 325 | } 326 | 327 | return $properties; 328 | } 329 | 330 | public static function logChanges(Model $model): array 331 | { 332 | $changes = []; 333 | $attributes = $model->attributesToBeLogged(); 334 | 335 | foreach ($attributes as $attribute) { 336 | if (Str::contains($attribute, '.')) { 337 | $changes += self::getRelatedModelAttributeValue($model, $attribute); 338 | 339 | continue; 340 | } 341 | 342 | if (Str::contains($attribute, '->')) { 343 | Arr::set( 344 | $changes, 345 | str_replace('->', '.', $attribute), 346 | static::getModelAttributeJsonValue($model, $attribute) 347 | ); 348 | 349 | continue; 350 | } 351 | 352 | $changes[$attribute] = in_array($attribute, $model->activitylogOptions->attributeRawValues) 353 | ? $model->getAttributeFromArray($attribute) 354 | : $model->getAttribute($attribute); 355 | 356 | if (is_null($changes[$attribute])) { 357 | continue; 358 | } 359 | 360 | if ($model->isDateAttribute($attribute)) { 361 | $changes[$attribute] = $model->serializeDate( 362 | $model->asDateTime($changes[$attribute]) 363 | ); 364 | } 365 | 366 | if ($model->hasCast($attribute)) { 367 | $cast = $model->getCasts()[$attribute]; 368 | 369 | if ($model->isEnumCastable($attribute)) { 370 | try { 371 | $changes[$attribute] = $model->getStorableEnumValue($changes[$attribute]); 372 | } catch (\ArgumentCountError $e) { 373 | // In Laravel 11, this method has an extra argument 374 | // https://github.com/laravel/framework/pull/47465 375 | $changes[$attribute] = $model->getStorableEnumValue($cast, $changes[$attribute]); 376 | } 377 | } 378 | 379 | if ($model->isCustomDateTimeCast($cast) || $model->isImmutableCustomDateTimeCast($cast)) { 380 | $changes[$attribute] = $model->asDateTime($changes[$attribute])->format(explode(':', $cast, 2)[1]); 381 | } 382 | } 383 | } 384 | 385 | return $changes; 386 | } 387 | 388 | protected static function getRelatedModelAttributeValue(Model $model, string $attribute): array 389 | { 390 | $relatedModelNames = explode('.', $attribute); 391 | $relatedAttribute = array_pop($relatedModelNames); 392 | 393 | $attributeName = []; 394 | $relatedModel = $model; 395 | 396 | do { 397 | $attributeName[] = $relatedModelName = static::getRelatedModelRelationName($relatedModel, array_shift($relatedModelNames)); 398 | 399 | $relatedModel = $relatedModel->$relatedModelName ?? $relatedModel->$relatedModelName(); 400 | } while (! empty($relatedModelNames)); 401 | 402 | $attributeName[] = $relatedAttribute; 403 | 404 | return [implode('.', $attributeName) => $relatedModel->$relatedAttribute ?? null]; 405 | } 406 | 407 | protected static function getRelatedModelRelationName(Model $model, string $relation): string 408 | { 409 | return Arr::first([ 410 | $relation, 411 | Str::snake($relation), 412 | Str::camel($relation), 413 | ], function (string $method) use ($model): bool { 414 | return method_exists($model, $method); 415 | }, $relation); 416 | } 417 | 418 | protected static function getModelAttributeJsonValue(Model $model, string $attribute): mixed 419 | { 420 | $path = explode('->', $attribute); 421 | $modelAttribute = array_shift($path); 422 | $modelAttribute = collect($model->getAttribute($modelAttribute)); 423 | 424 | return data_get($modelAttribute, implode('.', $path)); 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | useLog($logName); 14 | } 15 | 16 | return $log->logger(); 17 | } 18 | } 19 | --------------------------------------------------------------------------------