From e6c42c9e7fbfea7473a5d950bf12ac6629f6d94e Mon Sep 17 00:00:00 2001 From: leopado Date: Fri, 8 Apr 2022 12:11:46 +0200 Subject: [PATCH] Updated to version 4.x of spatie/laravel-activitylog --- .gitignore | 1 + .idea/laravel-activitylog-extended.iml | 4 + .idea/php-test-framework.xml | 1 + .idea/php.xml | 87 +- .phpunit.result.cache | 2 +- composer.json | 2 +- src/Models/Activity.php | 47 +- src/Traits/LogsActivityWithRelations.php | 24 +- src/Traits/RelationshipsTrait.php | 6 +- tests/ActivityloggerTest.php | 200 +++ tests/Casts/IntervalCasts.php | 29 + tests/CauserResolverTest.php | 64 + tests/CleanActivitylogCommandTest.php | 21 + ...tomDatabaseConnectionActivityModelTest.php | 58 + tests/CustomTableNameModelTest.php | 49 + tests/DetectsChangesTest.php | 1572 ++++++++++++++++- tests/HasActivityTest.php | 8 +- tests/LogBatchTest.php | 160 ++ tests/LogsActivityTest.php | 293 ++- tests/Models/ArticleWithRelations.php | 11 +- tests/Models/CustomActivityModel.php | 7 +- ...ustomDatabaseConnectionOnActivityModel.php | 10 + tests/Models/CustomInvalidActivityModel.php | 7 +- .../Models/CustomTableNameOnActivityModel.php | 10 + tests/Models/Issue733.php | 22 + tests/Models/User.php | 5 + tests/Models/UserWithRelations.php | 6 + tests/TestCase.php | 48 +- 28 files changed, 2669 insertions(+), 85 deletions(-) create mode 100644 tests/Casts/IntervalCasts.php create mode 100644 tests/CauserResolverTest.php create mode 100644 tests/CustomDatabaseConnectionActivityModelTest.php create mode 100644 tests/CustomTableNameModelTest.php create mode 100644 tests/LogBatchTest.php create mode 100644 tests/Models/CustomDatabaseConnectionOnActivityModel.php create mode 100644 tests/Models/CustomTableNameOnActivityModel.php create mode 100644 tests/Models/Issue733.php diff --git a/.gitignore b/.gitignore index f02a2f8..85d90cf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build composer.lock docs vendor +.idea diff --git a/.idea/laravel-activitylog-extended.iml b/.idea/laravel-activitylog-extended.iml index e50c58c..17b75cc 100644 --- a/.idea/laravel-activitylog-extended.iml +++ b/.idea/laravel-activitylog-extended.iml @@ -115,6 +115,10 @@ + + + + diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml index fc195b9..a01bbcb 100644 --- a/.idea/php-test-framework.xml +++ b/.idea/php-test-framework.xml @@ -6,6 +6,7 @@ + diff --git a/.idea/php.xml b/.idea/php.xml index 5acac42..ecde23c 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -112,9 +112,94 @@ + + + - + + + + + + + + + + + + /etc/php/8.1/cli/conf.d/10-mysqlnd.ini, /etc/php/8.1/cli/conf.d/10-opcache.ini, /etc/php/8.1/cli/conf.d/10-pdo.ini, /etc/php/8.1/cli/conf.d/15-xml.ini, /etc/php/8.1/cli/conf.d/20-ast.ini, /etc/php/8.1/cli/conf.d/20-bcmath.ini, /etc/php/8.1/cli/conf.d/20-calendar.ini, /etc/php/8.1/cli/conf.d/20-ctype.ini, /etc/php/8.1/cli/conf.d/20-curl.ini, /etc/php/8.1/cli/conf.d/20-dom.ini, /etc/php/8.1/cli/conf.d/20-exif.ini, /etc/php/8.1/cli/conf.d/20-ffi.ini, /etc/php/8.1/cli/conf.d/20-fileinfo.ini, /etc/php/8.1/cli/conf.d/20-ftp.ini, /etc/php/8.1/cli/conf.d/20-gd.ini, /etc/php/8.1/cli/conf.d/20-gettext.ini, /etc/php/8.1/cli/conf.d/20-iconv.ini, /etc/php/8.1/cli/conf.d/20-igbinary.ini, /etc/php/8.1/cli/conf.d/20-intl.ini, /etc/php/8.1/cli/conf.d/20-mbstring.ini, /etc/php/8.1/cli/conf.d/20-msgpack.ini, /etc/php/8.1/cli/conf.d/20-mysqli.ini, /etc/php/8.1/cli/conf.d/20-pdo_mysql.ini, /etc/php/8.1/cli/conf.d/20-pdo_pgsql.ini, /etc/php/8.1/cli/conf.d/20-pdo_sqlite.ini, /etc/php/8.1/cli/conf.d/20-pgsql.ini, /etc/php/8.1/cli/conf.d/20-phar.ini, /etc/php/8.1/cli/conf.d/20-posix.ini, /etc/php/8.1/cli/conf.d/20-readline.ini, /etc/php/8.1/cli/conf.d/20-redis.ini, /etc/php/8.1/cli/conf.d/20-shmop.ini, /etc/php/8.1/cli/conf.d/20-simplexml.ini, /etc/php/8.1/cli/conf.d/20-soap.ini, /etc/php/8.1/cli/conf.d/20-sockets.ini, /etc/php/8.1/cli/conf.d/20-sqlite3.ini, /etc/php/8.1/cli/conf.d/20-sysvmsg.ini, /etc/php/8.1/cli/conf.d/20-sysvsem.ini, /etc/php/8.1/cli/conf.d/20-sysvshm.ini, /etc/php/8.1/cli/conf.d/20-tokenizer.ini, /etc/php/8.1/cli/conf.d/20-xmlreader.ini, /etc/php/8.1/cli/conf.d/20-xmlwriter.ini, /etc/php/8.1/cli/conf.d/20-xsl.ini, /etc/php/8.1/cli/conf.d/20-zip.ini, /etc/php/8.1/cli/conf.d/25-memcached.ini, /etc/php/8.1/cli/conf.d/xdebug.ini + /etc/php/8.1/cli/php.ini + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.phpunit.result.cache b/.phpunit.result.cache index 42cba6c..3b52cb7 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -{"version":1,"defects":{"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_activities_from_a_specific_log":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_from_multiple_logs":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_from_multiple_logs_using_an_array":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_subject":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_all_log_items_related_to_subject":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_morphmapped_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_morphmapped_subject":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_log_an_activity_when_the_log_is_not_enabled":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_an_activity_when_enabled_option_is_null":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_to_the_default_log_by_default":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_to_a_specific_log":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_subject":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer_when_there_is_no_web_guard":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_with_properties":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_with_a_single_properties":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_translate_a_given_causer_id_to_an_object":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_throw_an_exception_if_it_cannot_translate_a_causer_id":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_use_the_logged_in_user_as_the_causer_by_default":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_replace_the_placeholders":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_replace_non_placeholders":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_returns_an_instance_of_the_activity_log_after_logging_when_using_a_custom_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CausesActivityTest::it_can_get_all_activity_for_the_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CleanActivitylogCommandTest::it_can_clean_the_activity_log":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_can_log_activity_using_a_custom_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_does_not_throw_an_exception_when_model_config_is_null":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_throws_an_exception_when_model_doesnt_implements_activity":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_throws_an_exception_when_model_doesnt_extend_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_doesnt_conlict_with_laravel_change_tracking":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_values_when_creating_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_relation_values_when_creating_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_empty_relation_when_creating_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_dirty_changes_only":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_dirty_changes_for_swapping_values":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_related_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_dirty_changes_when_updating_a_related_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_no_changes_when_not_logging_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_the_values_when_deleting_the_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_the_values_when_deleting_the_model_with_softdeletes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_of_array_casted_properties":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_nothing_as_loggable_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_text_as_loggable_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_fillable_as_loggable_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_both_fillable_and_log_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_for_loggable_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_with_relation":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_when_updating_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\HasActivityTest::it_can_log_activity_on_subject_by_same_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_creation_of_the_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_skip_logging_model_events_if_asked_to":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_switch_on_activity_logging_after_disabling_it":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_skip_logging_if_asked_to_for_update_method":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_an_update_of_the_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_deletion_of_a_model_without_softdeletes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_deletion_of_a_model_with_softdeletes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_restoring_of_a_model_with_softdeletes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_fetch_all_activity_for_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_fetch_soft_deleted_models":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_to_log_returned_from_model_method_override":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_to_log_named_in_the_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_log_an_update_of_the_model_if_only_ignored_attributes_are_changed":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_fail_if_asked_to_replace_from_empty_attribute":4},"times":{"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_activities_from_a_specific_log":0.316,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_from_multiple_logs":0.098,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_from_multiple_logs_using_an_array":0.11,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_causer":0.143,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_subject":0.116,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_all_log_items_related_to_subject":0.129,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_morphmapped_causer":0.118,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_morphmapped_subject":0.108,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity":0.078,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_log_an_activity_when_the_log_is_not_enabled":0.075,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_an_activity_when_enabled_option_is_null":0.082,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_to_the_default_log_by_default":0.086,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_to_a_specific_log":0.086,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_subject":0.08,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer":0.08,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer_when_there_is_no_web_guard":0.08,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_with_properties":0.081,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_with_a_single_properties":0.083,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_translate_a_given_causer_id_to_an_object":0.083,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_throw_an_exception_if_it_cannot_translate_a_causer_id":0.095,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_use_the_logged_in_user_as_the_causer_by_default":0.227,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_replace_the_placeholders":0.258,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_replace_non_placeholders":0.16,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_returns_an_instance_of_the_activity_log_after_logging_when_using_a_custom_model":0.088,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CausesActivityTest::it_can_get_all_activity_for_the_causer":0.107,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CleanActivitylogCommandTest::it_can_clean_the_activity_log":0.573,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_can_log_activity_using_a_custom_model":0.108,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_does_not_throw_an_exception_when_model_config_is_null":0.104,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_throws_an_exception_when_model_doesnt_implements_activity":0.101,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_throws_an_exception_when_model_doesnt_extend_model":0.097,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_doesnt_conlict_with_laravel_change_tracking":0.102,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_values_when_creating_a_model":0.088,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_relation_values_when_creating_a_model":0.098,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_empty_relation_when_creating_a_model":0.101,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_model":0.094,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_dirty_changes_only":0.095,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_dirty_changes_for_swapping_values":0.093,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_related_model":0.101,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_dirty_changes_when_updating_a_related_model":0.103,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_no_changes_when_not_logging_attributes":0.089,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_the_values_when_deleting_the_model":0.23,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_the_values_when_deleting_the_model_with_softdeletes":0.315,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_of_array_casted_properties":0.243,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_nothing_as_loggable_attributes":0.084,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_text_as_loggable_attributes":0.083,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_fillable_as_loggable_attributes":0.085,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_both_fillable_and_log_attributes":0.086,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_for_loggable_attributes":0.087,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_with_relation":0.088,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_when_updating_model":0.104,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\HasActivityTest::it_can_log_activity_on_subject_by_same_causer":0.084,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_creation_of_the_model":0.085,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_skip_logging_model_events_if_asked_to":0.081,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_switch_on_activity_logging_after_disabling_it":0.143,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_skip_logging_if_asked_to_for_update_method":0.233,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_an_update_of_the_model":0.243,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_deletion_of_a_model_without_softdeletes":0.09,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_deletion_of_a_model_with_softdeletes":0.099,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_restoring_of_a_model_with_softdeletes":0.105,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_fetch_all_activity_for_a_model":0.098,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_fetch_soft_deleted_models":0.105,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_to_log_returned_from_model_method_override":0.089,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_to_log_named_in_the_model":0.094,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_log_an_update_of_the_model_if_only_ignored_attributes_are_changed":0.102,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_fail_if_asked_to_replace_from_empty_attribute":0.098}} \ No newline at end of file +{"version":1,"defects":{"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_activities_from_a_specific_log":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_from_multiple_logs":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_from_multiple_logs_using_an_array":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_subject":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_all_log_items_related_to_subject":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_morphmapped_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_morphmapped_subject":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_log_an_activity_when_the_log_is_not_enabled":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_an_activity_when_enabled_option_is_null":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_to_the_default_log_by_default":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_to_a_specific_log":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_subject":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer_when_there_is_no_web_guard":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_with_properties":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_with_a_single_properties":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_translate_a_given_causer_id_to_an_object":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_throw_an_exception_if_it_cannot_translate_a_causer_id":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_use_the_logged_in_user_as_the_causer_by_default":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_replace_the_placeholders":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_replace_non_placeholders":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_returns_an_instance_of_the_activity_log_after_logging_when_using_a_custom_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CausesActivityTest::it_can_get_all_activity_for_the_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CleanActivitylogCommandTest::it_can_clean_the_activity_log":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_can_log_activity_using_a_custom_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_does_not_throw_an_exception_when_model_config_is_null":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_throws_an_exception_when_model_doesnt_implements_activity":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_throws_an_exception_when_model_doesnt_extend_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_doesnt_conlict_with_laravel_change_tracking":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_values_when_creating_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_relation_values_when_creating_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_empty_relation_when_creating_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_dirty_changes_only":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_dirty_changes_for_swapping_values":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_related_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_dirty_changes_when_updating_a_related_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_no_changes_when_not_logging_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_the_values_when_deleting_the_model":3,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_the_values_when_deleting_the_model_with_softdeletes":3,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_of_array_casted_properties":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_nothing_as_loggable_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_text_as_loggable_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_fillable_as_loggable_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_both_fillable_and_log_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_for_loggable_attributes":3,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_with_relation":3,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_when_updating_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\HasActivityTest::it_can_log_activity_on_subject_by_same_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_creation_of_the_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_skip_logging_model_events_if_asked_to":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_switch_on_activity_logging_after_disabling_it":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_skip_logging_if_asked_to_for_update_method":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_an_update_of_the_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_deletion_of_a_model_without_softdeletes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_deletion_of_a_model_with_softdeletes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_restoring_of_a_model_with_softdeletes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_fetch_all_activity_for_a_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_fetch_soft_deleted_models":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_to_log_returned_from_model_method_override":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_to_log_named_in_the_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_log_an_update_of_the_model_if_only_ignored_attributes_are_changed":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_fail_if_asked_to_replace_from_empty_attribute":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_when_attributes_are_changed_with_tap":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_disable_logs_for_a_callback_without_affecting_previous_state_even_with_exception":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer_other_than_user_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer_that_has_been_set_from_other_context":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_using_an_anonymous_causer":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_override_the_logged_in_user_as_the_causer_when_an_anonymous_causer_is_specified":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_event":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_log_an_activity_when_the_log_is_manually_disabled":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_an_activity_when_the_log_is_manually_enabled":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_accepts_null_parameter_for_caused_by":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_a_custom_created_at_date_time":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_disable_logs_for_a_callback":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_disable_logs_for_a_callback_without_affecting_previous_state":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_disable_logs_for_a_callback_without_affecting_previous_state_even_when_already_disabled":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_can_resolve_current_logged_in_user":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_will_throw_an_exception_if_it_cannot_resolve_user_by_id":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_can_resloved_user_from_passed_id":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_will_resolve_the_provided_override_callback":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_will_resolve_any_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomDatabaseConnectionActivityModelTest::it_uses_the_database_connection_from_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomTableNameModelTest::uses_the_table_name_from_the_configuration":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomTableNameModelTest::uses_a_custom_table_name":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomTableNameModelTest::uses_the_table_name_from_the_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_when_attributes_are_changed_with_tap":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_when_description_is_changed_with_tap":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_when_event_is_changed_with_tap":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_retrieval_of_the_model":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_detect_changes_for_date_inteval_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_removes_key_event_if_it_was_loggable":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_saving_including_multi_level_related_model":3,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_overloaded_as_loggable_attributes":4,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_detect_changes_for_null_date_inteval_attributes":3},"times":{"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_activities_from_a_specific_log":1.615,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_from_multiple_logs":0.584,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_from_multiple_logs_using_an_array":0.58,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_causer":0.694,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_subject":0.682,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_all_log_items_related_to_subject":0.781,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_morphmapped_causer":0.682,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityModelTest::it_provides_a_scope_to_get_log_items_for_a_specific_morphmapped_subject":0.695,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity":0.453,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_log_an_activity_when_the_log_is_not_enabled":0.434,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_an_activity_when_enabled_option_is_null":0.459,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_to_the_default_log_by_default":0.467,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_to_a_specific_log":0.5,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_subject":0.471,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer":0.465,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer_when_there_is_no_web_guard":0.468,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_with_properties":0.475,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_with_a_single_properties":0.465,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_translate_a_given_causer_id_to_an_object":0.647,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_throw_an_exception_if_it_cannot_translate_a_causer_id":0.443,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_use_the_logged_in_user_as_the_causer_by_default":0.561,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_replace_the_placeholders":0.573,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_replace_non_placeholders":0.68,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_returns_an_instance_of_the_activity_log_after_logging_when_using_a_custom_model":0.647,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CausesActivityTest::it_can_get_all_activity_for_the_causer":0.8,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CleanActivitylogCommandTest::it_can_clean_the_activity_log":3.147,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_can_log_activity_using_a_custom_model":0.722,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_does_not_throw_an_exception_when_model_config_is_null":0.719,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_throws_an_exception_when_model_doesnt_implements_activity":0.688,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_throws_an_exception_when_model_doesnt_extend_model":0.682,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomActivityModelTest::it_doesnt_conlict_with_laravel_change_tracking":0.741,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_values_when_creating_a_model":0.594,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_relation_values_when_creating_a_model":0.8,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_empty_relation_when_creating_a_model":0.817,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_model":0.749,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_dirty_changes_only":0.729,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_dirty_changes_for_swapping_values":0.909,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_related_model":0.906,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_dirty_changes_when_updating_a_related_model":0.728,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_no_changes_when_not_logging_attributes":0.601,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_the_values_when_deleting_the_model":0.692,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_the_values_when_deleting_the_model_with_softdeletes":0.748,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_of_array_casted_properties":0.66,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_nothing_as_loggable_attributes":0.588,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_text_as_loggable_attributes":0.579,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_fillable_as_loggable_attributes":0.691,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_both_fillable_and_log_attributes":0.635,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_for_loggable_attributes":0.592,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_with_relation":0.7,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_wildcard_when_updating_model":0.9,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\HasActivityTest::it_can_log_activity_on_subject_by_same_causer":0.628,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_creation_of_the_model":0.77,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_skip_logging_model_events_if_asked_to":0.647,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_switch_on_activity_logging_after_disabling_it":0.744,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_skip_logging_if_asked_to_for_update_method":0.651,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_an_update_of_the_model":0.922,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_deletion_of_a_model_without_softdeletes":0.696,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_deletion_of_a_model_with_softdeletes":0.785,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_restoring_of_a_model_with_softdeletes":0.832,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_fetch_all_activity_for_a_model":0.674,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_fetch_soft_deleted_models":0.75,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_to_log_returned_from_model_method_override":0.618,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_to_log_named_in_the_model":0.575,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_log_an_update_of_the_model_if_only_ignored_attributes_are_changed":0.636,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_fail_if_asked_to_replace_from_empty_attribute":0.672,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer_other_than_user_model":0.46,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_a_causer_that_has_been_set_from_other_context":0.476,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_using_an_anonymous_causer":0.52,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_override_the_logged_in_user_as_the_causer_when_an_anonymous_causer_is_specified":0.474,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_an_activity_with_event":0.94,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_not_log_an_activity_when_the_log_is_manually_disabled":0.553,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_an_activity_when_the_log_is_manually_enabled":0.554,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_accepts_null_parameter_for_caused_by":0.556,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_can_log_activity_when_attributes_are_changed_with_tap":0.639,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_log_a_custom_created_at_date_time":0.605,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_disable_logs_for_a_callback":0.531,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_disable_logs_for_a_callback_without_affecting_previous_state":0.659,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_disable_logs_for_a_callback_without_affecting_previous_state_even_when_already_disabled":0.602,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\ActivityloggerTest::it_will_disable_logs_for_a_callback_without_affecting_previous_state_even_with_exception":0.556,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_can_resolve_current_logged_in_user":0.543,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_will_throw_an_exception_if_it_cannot_resolve_user_by_id":0.538,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_can_resloved_user_from_passed_id":0.603,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_will_resolve_the_provided_override_callback":0.862,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CauserResolverTest::it_will_resolve_any_model":0.873,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CleanActivitylogCommandTest::it_can_accept_days_as_option_to_override_config_setting":2.854,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomDatabaseConnectionActivityModelTest::it_uses_the_database_connection_from_the_configuration":0.806,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomDatabaseConnectionActivityModelTest::it_uses_a_custom_database_connection":0.758,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomDatabaseConnectionActivityModelTest::it_uses_the_default_database_connection_when_the_one_from_configuration_is_null":0.805,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomDatabaseConnectionActivityModelTest::it_uses_the_database_connection_from_model":0.842,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomTableNameModelTest::uses_the_table_name_from_the_configuration":0.843,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomTableNameModelTest::uses_a_custom_table_name":0.783,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\CustomTableNameModelTest::uses_the_table_name_from_the_model":0.689,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogBatchTest::it_generates_uuid_after_start_and_end_batch_properely":0.701,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogBatchTest::it_returns_null_uuid_after_end_batch_properely":0.697,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogBatchTest::it_generates_a_new_uuid_after_starting_new_batch_properly":0.698,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogBatchTest::it_will_not_generate_new_uuid_if_start_already_started_batch":0.713,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogBatchTest::it_will_not_generate_uuid_if_end_batch_before_starting":0.732,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogBatchTest::it_can_set_uuid_and_start_a_batch":0.709,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogBatchTest::it_can_set_uuid_for_already_started_batch":0.728,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogBatchTest::it_will_not_return_null_uuid_if_end_batch_that_started_twice":0.697,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogBatchTest::it_will_return_null_uuid_if_end_batch_that_started_twice_properly":0.679,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::can_log_activity_on_subject_by_same_causer":0.941,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_when_attributes_are_changed_with_tap":0.735,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_when_description_is_changed_with_tap":0.609,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_can_log_activity_when_event_is_changed_with_tap":0.573,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_submit_log_when_there_is_no_changes":0.624,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_submit_a_log_with_json_changes":0.68,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_log_the_retrieval_of_the_model":0.637,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\LogsActivityTest::it_will_not_log_casted_attribute_of_the_model_if_attribute_raw_values_is_used":0.607,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_deep_diff_check_json_field":0.639,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_detect_changes_for_date_inteval_attributes":0.677,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_detect_changes_for_null_date_inteval_attributes":0.925,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_retruns_same_uuid_for_all_log_changes_under_one_batch":1.078,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_assigns_new_uuid_for_multiple_change_logs_in_different_batches":0.858,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_removes_key_event_if_it_was_loggable":0.772,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_snake_case_related_model":0.711,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_camel_case_related_model":0.712,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_updating_a_custom_case_related_model":0.881,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_saving_including_multi_level_related_model":0.877,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_of_collection_casted_properties":0.835,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_of_json_casted_properties":0.732,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_when_a_boolean_field_is_changed_from_false_to_null":0.806,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_ignored_attributes_while_updating":0.602,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_unguarded_as_loggable_attributes":0.578,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_store_no_changes_when_wildcard_guard_and_log_unguarded_attributes":0.581,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_hidden_as_loggable_attributes":0.6,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_overloaded_as_loggable_attributes":0.705,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_mutated_as_loggable_attributes":0.749,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_accessor_as_loggable_attributes":0.706,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_encrypted_as_loggable_attributes":0.779,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_casted_as_loggable_attribute":0.821,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_nullable_date_as_loggable_attributes":0.646,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_custom_date_cast_as_loggable_attributes":0.619,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_use_custom_immutable_date_cast_as_loggable_attributes":0.587,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_can_store_the_changes_of_json_attributes":0.618,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_not_store_changes_to_untracked_json":0.856,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_return_null_for_missing_json_attribute":0.728,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_return_an_array_for_sub_key_in_json_attribute":0.646,"Padosoft\\Laravel\\ActivitylogExtended\\Test\\DetectsChangesTest::it_will_access_further_than_level_one_json_attribute":0.657}} \ No newline at end of file diff --git a/composer.json b/composer.json index 4475223..702c5d2 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "spatie/laravel-activitylog": "^3.6" + "spatie/laravel-activitylog": "^4.0" }, "require-dev": { "roave/security-advisories": "dev-latest", diff --git a/src/Models/Activity.php b/src/Models/Activity.php index 87cbfa6..958a3eb 100644 --- a/src/Models/Activity.php +++ b/src/Models/Activity.php @@ -10,8 +10,40 @@ use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Request; +/** + * Padosoft\Laravel\ActivitylogExtended\Models\Activity. + * + * @property int $id + * @property string|null $log_name + * @property string $description + * @property string|null $subject_type + * @property int|null $subject_id + * @property string|null $causer_type + * @property int|null $causer_id + * @property \Illuminate\Support\Collection|null $properties + * @property \Carbon\Carbon|null $created_at + * @property \Carbon\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $causer + * @property-read \Illuminate\Support\Collection $changes + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $subject + * + * @method static \Illuminate\Database\Eloquent\Builder|\Spatie\Activitylog\Models\Activity causedBy(\Illuminate\Database\Eloquent\Model $causer) + * @method static \Illuminate\Database\Eloquent\Builder|\Spatie\Activitylog\Models\Activity forBatch(string $batchUuid) + * @method static \Illuminate\Database\Eloquent\Builder|\Spatie\Activitylog\Models\Activity forEvent(string $event) + * @method static \Illuminate\Database\Eloquent\Builder|\Spatie\Activitylog\Models\Activity forSubject(\Illuminate\Database\Eloquent\Model $subject) + * @method static \Illuminate\Database\Eloquent\Builder|\Spatie\Activitylog\Models\Activity hasBatch() + * @method static \Illuminate\Database\Eloquent\Builder|\Spatie\Activitylog\Models\Activity inLog($logNames) + * @method static \Illuminate\Database\Eloquent\Builder|\Spatie\Activitylog\Models\Activity newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\Spatie\Activitylog\Models\Activity newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\Spatie\Activitylog\Models\Activity query() + */ class Activity extends ActivityBase { + public function __construct(array $attributes = []) + { + parent::__construct($attributes); + } + protected static function boot() { parent::boot(); @@ -22,17 +54,17 @@ protected static function boot() }); } - public function resolveIp() + public function resolveIp(): ?string { return Request::ip(); } - public function resolveUserAgent() + public function resolveUserAgent(): array|string|null { return Request::header('User-Agent'); } - public function resolveUrl() + public function resolveUrl(): string { if (!App::runningInConsole()) { return Request::fullUrlWithQuery([]); @@ -44,20 +76,15 @@ public function resolveUrl() return 'console'; } - public function __construct(array $attributes = []) - { - parent::__construct($attributes); - } - public function scopeAllRelations(Builder $query, \Illuminate\Database\Eloquent\Model $subject): Builder { return $query ->where(function ($q) use ($subject) { $q->where('subject_type', '=', $subject->getMorphClass()) - ->where('subject_id', $subject->getKey()); + ->where('subject_id', $subject->getKey()); })->orWhere(function ($q) use ($subject) { $q->where('causer_type', '=', $subject->getMorphClass()) - ->where('causer_id', $subject->getKey()); + ->where('causer_id', $subject->getKey()); })->orWhere('properties', 'LIKE', '%' . $subject->getTable() . '":"' . $subject->getKey() . '"%'); } } diff --git a/src/Traits/LogsActivityWithRelations.php b/src/Traits/LogsActivityWithRelations.php index e4a08a6..102caea 100644 --- a/src/Traits/LogsActivityWithRelations.php +++ b/src/Traits/LogsActivityWithRelations.php @@ -23,6 +23,10 @@ trait LogsActivityWithRelations self::bootLogsActivity(); }*/ + /** + * @param string $processingEvent + * @return array + */ public function attributeValuesToBeLogged(string $processingEvent): array { $properties = $this->attributeValuesToBeLoggedBase($processingEvent); @@ -32,19 +36,29 @@ public function attributeValuesToBeLogged(string $processingEvent): array return $properties; } + + /** + * @return mixed + */ public function getAllRelatedActivites() { $model = ActivitylogServiceProvider::getActivityModelInstance(); - - return $model->allRelations($this)->get(); + $subject = $this; + return $model->where(function ($q) use ($subject) { + $q->where('subject_type', '=', $subject->getMorphClass()) + ->where('subject_id', $subject->getKey()); + })->orWhere(function ($q) use ($subject) { + $q->where('causer_type', '=', $subject->getMorphClass()) + ->where('causer_id', $subject->getKey()); + })->orWhere('properties', 'LIKE', '%' . $subject->getTable() . '":"' . $subject->getKey() . '"%')->get(); } /** - * @param $properties + * @param array $properties * - * @return mixed + * @return array */ - public function setRelationsToBeLogged($properties) + public function setRelationsToBeLogged(array $properties): array { $relationships = $this->getModelRelations(); diff --git a/src/Traits/RelationshipsTrait.php b/src/Traits/RelationshipsTrait.php index 61ecaa2..37f2a94 100644 --- a/src/Traits/RelationshipsTrait.php +++ b/src/Traits/RelationshipsTrait.php @@ -13,7 +13,11 @@ trait RelationshipsTrait { - public function getModelRelations() + /** + * @return array + * @throws \ReflectionException + */ + public function getModelRelations(): array { $model = new static; diff --git a/tests/ActivityloggerTest.php b/tests/ActivityloggerTest.php index aeef08e..8d56344 100644 --- a/tests/ActivityloggerTest.php +++ b/tests/ActivityloggerTest.php @@ -3,11 +3,14 @@ namespace Padosoft\Laravel\ActivitylogExtended\Test; use Auth; +use Carbon\Carbon; use Illuminate\Support\Collection; use Padosoft\Laravel\ActivitylogExtended\Models\Activity; use Padosoft\Laravel\ActivitylogExtended\Test\Models\User; use Padosoft\Laravel\ActivitylogExtended\Test\Models\Article; +use Spatie\Activitylog\Contracts\Activity as ActivityContract; use Spatie\Activitylog\Exceptions\CouldNotLogActivity; +use Spatie\Activitylog\Facades\CauserResolver; class ActivityloggerTest extends TestCase { @@ -99,6 +102,35 @@ public function it_can_log_an_activity_with_a_causer() $this->assertInstanceOf(User::class, $firstActivity->causer); } + /** @test */ + public function it_can_log_an_activity_with_a_causer_other_than_user_model() + { + $article = Article::first(); + activity() + ->causedBy($article) + ->log($this->activityDescription); + $firstActivity = Activity::first(); + $this->assertEquals($firstActivity->causer->id, $article->id); + $this->assertInstanceOf(Article::class, $firstActivity->causer); + } + + /** @test */ + public function it_can_log_an_activity_with_a_causer_that_has_been_set_from_other_context() + { + $causer = Article::first(); + CauserResolver::setCauser($causer); + + $article = Article::first(); + + activity() + ->log($this->activityDescription); + + $firstActivity = Activity::first(); + + $this->assertEquals($firstActivity->causer->id, $article->id); + $this->assertInstanceOf(Article::class, $firstActivity->causer); + } + /** @test */ public function it_can_log_an_activity_with_a_causer_when_there_is_no_web_guard() { @@ -194,6 +226,29 @@ public function it_will_use_the_logged_in_user_as_the_causer_by_default() $this->assertEquals($userId, $this->getLastActivity()->causer->id); } + /** @test */ + public function it_can_log_activity_using_an_anonymous_causer() + { + activity() + ->causedByAnonymous() + ->log('hello poetsvrouwman'); + $this->assertNull($this->getLastActivity()->causer_id); + $this->assertNull($this->getLastActivity()->causer_type); + } + + /** @test */ + public function it_will_override_the_logged_in_user_as_the_causer_when_an_anonymous_causer_is_specified() + { + $userId = 1; + + Auth::login(User::find($userId)); + activity() + ->byAnonymous() + ->log('hello poetsvrouwman'); + $this->assertNull($this->getLastActivity()->causer_id); + $this->assertNull($this->getLastActivity()->causer_type); + } + /** @test */ public function it_can_replace_the_placeholders() { @@ -212,6 +267,17 @@ public function it_can_replace_the_placeholders() $this->assertEquals($expectedDescription, $this->getLastActivity()->description); } + /** @test */ + public function it_can_log_an_activity_with_event() + { + $article = Article::create(['name' => 'article name']); + activity() + ->performedOn($article) + ->event('create') + ->log('test event'); + $this->assertEquals('create', $this->getLastActivity()->event); + } + /** @test */ public function it_will_not_replace_non_placeholders() { @@ -243,4 +309,138 @@ public function it_returns_an_instance_of_the_activity_log_after_logging_when_us $this->assertInstanceOf($activityClassName, $activityModel); } + + /** @test */ + public function it_will_not_log_an_activity_when_the_log_is_manually_disabled() + { + activity()->disableLogging(); + + activity()->log($this->activityDescription); + + $this->assertNull($this->getLastActivity()); + } + + /** @test */ + public function it_will_log_an_activity_when_the_log_is_manually_enabled() + { + config(['activitylog.enabled' => false]); + + activity()->enableLogging(); + + activity()->log($this->activityDescription); + + $this->assertEquals($this->getLastActivity()->description, $this->activityDescription); + } + + /** @test */ + public function it_accepts_null_parameter_for_caused_by() + { + activity()->causedBy(null)->log('nothing'); + + $this->assertTrue(true); + } + + /** @test */ + public function it_can_log_activity_when_attributes_are_changed_with_tap() + { + $properties = [ + 'property' => [ + 'subProperty' => 'value', + ], + ]; + + activity() + ->tap(function (ActivityContract $activity) use ($properties) { + $activity->properties = collect($properties); + $activity->created_at = Carbon::yesterday()->startOfDay(); + }) + ->log($this->activityDescription); + + $firstActivity = Activity::first(); + + + $this->assertInstanceOf(Collection::class, $firstActivity->properties); + + $this->assertEquals($firstActivity->getExtraProperty('property.subProperty'), 'value'); + $this->assertEquals($firstActivity->created_at->format('Y-m-d H:i:s'), Carbon::yesterday()->startOfDay()->format('Y-m-d H:i:s')); + } + + /** @test */ + public function it_will_log_a_custom_created_at_date_time() + { + $activityDateTime = now()->subDays(10); + + activity() + ->createdAt($activityDateTime) + ->log('created'); + + $firstActivity = Activity::first(); + + $this->assertEquals($firstActivity->created_at->toAtomString(), $activityDateTime->toAtomString()); + } + + /** @test */ + public function it_will_disable_logs_for_a_callback() + { + $result = activity()->withoutLogs(function () { + activity()->log('created'); + + return 'hello'; + }); + + $this->assertNull($this->getLastActivity()); + $this->assertEquals('hello', $result); + } + + /** @test */ + public function it_will_disable_logs_for_a_callback_without_affecting_previous_state() + { + activity()->withoutLogs(function () { + activity()->log('created'); + }); + + $this->assertNull($this->getLastActivity()); + + activity()->log('outer'); + + $this->assertEquals('outer', $this->getLastActivity()->description); + } + + /** @test */ + public function it_will_disable_logs_for_a_callback_without_affecting_previous_state_even_when_already_disabled() + { + activity()->disableLogging(); + + activity()->withoutLogs(function () { + activity()->log('created'); + }); + + $this->assertNull($this->getLastActivity()); + + activity()->log('outer'); + + $this->assertNull($this->getLastActivity()); + } + + /** @test */ + public function it_will_disable_logs_for_a_callback_without_affecting_previous_state_even_with_exception() + { + activity()->disableLogging(); + + try { + activity()->withoutLogs(function () { + activity()->log('created'); + + throw new \Exception('OH NO'); + }); + } catch (\Exception $ex) { + // + } + + $this->assertNull($this->getLastActivity()); + + activity()->log('outer'); + + $this->assertNull($this->getLastActivity()); + } } diff --git a/tests/Casts/IntervalCasts.php b/tests/Casts/IntervalCasts.php new file mode 100644 index 0000000..ac1c501 --- /dev/null +++ b/tests/Casts/IntervalCasts.php @@ -0,0 +1,29 @@ +assertInstanceOf(User::class, $causer); + $this->assertEquals($causer->id, $user->id); + } + + /** @test */ + public function it_will_throw_an_exception_if_it_cannot_resolve_user_by_id() + { + $this->expectException(CouldNotLogActivity::class); + + CauserResolver::resolve(9999); + } + + /** @test */ + public function it_can_resloved_user_from_passed_id() + { + $causer = CauserResolver::resolve(1); + + $this->assertInstanceOf(User::class, $causer); + $this->assertEquals(1, $causer->id); + } + + /** @test */ + public function it_will_resolve_the_provided_override_callback() + { + CauserResolver::resolveUsing(fn() => Article::first()); + + $causer = CauserResolver::resolve(); + + $this->assertInstanceOf(Article::class, $causer); + $this->assertEquals(1, $causer->id); + } + + /** @test */ + public function it_will_resolve_any_model() + { + $causer = CauserResolver::resolve($article = Article::first()); + + $this->assertInstanceOf(Article::class, $causer); + $this->assertEquals($article->id, $causer->id); + } +} diff --git a/tests/CleanActivitylogCommandTest.php b/tests/CleanActivitylogCommandTest.php index 46e23f8..aa5bfff 100644 --- a/tests/CleanActivitylogCommandTest.php +++ b/tests/CleanActivitylogCommandTest.php @@ -37,4 +37,25 @@ public function it_can_clean_the_activity_log() $this->assertCount(0, Activity::where('created_at', '<', $cutOffDate)->get()); } + + /** @test */ + public function it_can_accept_days_as_option_to_override_config_setting() + { + collect(range(1, 60))->each(function (int $index) { + Activity::create([ + 'description' => "item {$index}", + 'created_at' => Carbon::now()->subDays($index)->startOfDay(), + ]); + }); + + $this->assertCount(60, Activity::all()); + + Artisan::call('activitylog:clean', ['--days' => 7]); + + $this->assertCount(7, Activity::all()); + + $cutOffDate = Carbon::now()->subDays(7)->format('Y-m-d H:i:s'); + + $this->assertCount(0, Activity::where('created_at', '<', $cutOffDate)->get()); + } } diff --git a/tests/CustomDatabaseConnectionActivityModelTest.php b/tests/CustomDatabaseConnectionActivityModelTest.php new file mode 100644 index 0000000..684bccb --- /dev/null +++ b/tests/CustomDatabaseConnectionActivityModelTest.php @@ -0,0 +1,58 @@ +activityDescription = 'My activity'; + parent::setUp(); + + collect(range(1, 5))->each(function (int $index) { + $logName = "log{$index}"; + activity($logName)->log('hello everybody'); + }); + } + + /** @test */ + public function it_uses_the_database_connection_from_the_configuration() + { + $model = new Activity(); + + $this->assertEquals(config('activitylog.database_connection'), $model->getConnectionName()); + } + + /** @test */ + public function it_uses_a_custom_database_connection() + { + $model = new Activity(); + + $model->setConnection('custom_sqlite'); + + $this->assertNotEquals($model->getConnectionName(), config('activitylog.database_connection')); + $this->assertEquals('custom_sqlite', $model->getConnectionName()); + } + + /** @test */ + public function it_uses_the_default_database_connection_when_the_one_from_configuration_is_null() + { + app()['config']->set('activitylog.database_connection', null); + + $model = new Activity(); + + $this->assertInstanceOf('Illuminate\Database\SQLiteConnection', $model->getConnection()); + } + + /** @test */ + public function it_uses_the_database_connection_from_model() + { + $model = new CustomDatabaseConnectionOnActivityModel(); + + $this->assertNotEquals($model->getConnectionName(), config('activitylog.database_connection')); + $this->assertEquals('custom_connection_name', $model->getConnectionName()); + } +} diff --git a/tests/CustomTableNameModelTest.php b/tests/CustomTableNameModelTest.php new file mode 100644 index 0000000..3effa34 --- /dev/null +++ b/tests/CustomTableNameModelTest.php @@ -0,0 +1,49 @@ +activityDescription = 'My activity'; + parent::setUp(); + + collect(range(1, 5))->each(function (int $index) { + $logName = "log{$index}"; + activity($logName)->log('hello everybody'); + }); + } + + /** @test */ + public function uses_the_table_name_from_the_configuration() + { + $model = new Activity(); + + $this->assertEquals(config('activitylog.table_name'), $model->getTable()); + } + + /** @test */ + public function uses_a_custom_table_name() + { + $model = new Activity(); + $newTableName = 'my_personal_activities'; + + $model->setTable($newTableName); + + $this->assertNotEquals($model->getTable(), config('activitylog.table_name')); + $this->assertEquals($newTableName, $model->getTable()); + } + + /** @test */ + public function uses_the_table_name_from_the_model() + { + $model = new CustomTableNameOnActivityModel(); + + $this->assertNotEquals($model->getTable(), config('activitylog.table_name')); + $this->assertEquals('custom_table_name', $model->getTable()); + } +} diff --git a/tests/DetectsChangesTest.php b/tests/DetectsChangesTest.php index 464b4ea..9e6f05d 100644 --- a/tests/DetectsChangesTest.php +++ b/tests/DetectsChangesTest.php @@ -3,9 +3,16 @@ namespace Padosoft\Laravel\ActivitylogExtended\Test; use Carbon\Carbon; +use Carbon\CarbonInterval; +use Illuminate\Support\Arr; use Padosoft\Laravel\ActivitylogExtended\Models\Activity; +use Padosoft\Laravel\ActivitylogExtended\Test\Casts\IntervalCasts; use Padosoft\Laravel\ActivitylogExtended\Test\Models\User; use Padosoft\Laravel\ActivitylogExtended\Test\Models\Article; +use Spatie\Activitylog\Contracts\LoggablePipe; +use Spatie\Activitylog\EventLogBag; +use Spatie\Activitylog\LogBatch; +use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\LogsActivity; use Illuminate\Database\Eloquent\SoftDeletes; @@ -19,9 +26,13 @@ public function setUp(): void parent::setUp(); $this->article = new class() extends Article { - public static $logAttributes = ['name', 'text']; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['name', 'text']); + } }; $this->assertCount(0, Activity::all()); @@ -42,13 +53,124 @@ public function it_can_store_the_values_when_creating_a_model() $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); } + /** @test */ + public function it_deep_diff_check_json_field() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'json' => 'collection', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->dontSubmitEmptyLogs() + ->logOnlyDirty() + ->logOnly(['json->phone', 'json->details', 'json->address']); + } + }; + + $articleClass::addLogChange(new class() implements LoggablePipe { + public function handle(EventLogBag $event, \Closure $next): EventLogBag + { + if ($event->event === 'updated') { + $event->changes['attributes']['json'] = array_udiff_assoc( + $event->changes['attributes']['json'], + $event->changes['old']['json'], + function ($new, $old) { + if ($old === null || $new === null) { + return 0; + } + + return $new <=> $old; + } + ); + + $event->changes['old']['json'] = collect($event->changes['old']['json']) + ->only(array_keys($event->changes['attributes']['json'])) + ->all(); + } + + return $next($event); + } + }); + + $article = $articleClass::create([ + 'name' => 'Hamburg', + 'json' => ['details' => '', 'phone' => '1231231234', 'address' => 'new address'], + ]); + + $article->update(['json' => ['details' => 'new details']]); + + $expectedChanges = [ + 'attributes' => [ + 'json' => [ + 'details' => 'new details', + ], + ], + 'old' => [ + 'json' => [ + 'details' => '', + ], + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public function it_detect_changes_for_date_inteval_attributes() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'interval' => IntervalCasts::class, + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'interval']) + ->logOnlyDirty(); + } + }; + + $article = $articleClass::create([ + 'name' => 'Hamburg', + 'interval' => CarbonInterval::minute(), + ]); + + $article->update(['name' => 'New name', 'interval' => CarbonInterval::month()]); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'New name', + 'interval' => '1 month', + ], + 'old' => [ + 'name' => 'Hamburg', + 'interval' => '1 minute', + ], + ]; + + // test case when intervals changing from interval to another + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + /** @test */ public function it_can_store_the_relation_values_when_creating_a_model() { $articleClass = new class() extends Article { - public static $logAttributes = ['name', 'text', 'user.name']; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['name', 'text', 'user.name']); + } }; $user = User::create([ @@ -85,9 +207,13 @@ public function it_can_store_the_relation_values_when_creating_a_model() public function it_can_store_empty_relation_when_creating_a_model() { $articleClass = new class() extends Article { - public static $logAttributes = ['name', 'text', 'user.name']; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['name', 'text', 'user.name']); + } }; $user = User::create([ @@ -195,9 +321,13 @@ public function it_can_store_dirty_changes_for_swapping_values() public function it_can_store_the_changes_when_updating_a_related_model() { $articleClass = new class() extends Article { - public static $logAttributes = ['name', 'text', 'user.name']; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['name', 'text', 'user.name']); + } }; $user = User::create([ @@ -236,11 +366,13 @@ public function it_can_store_the_changes_when_updating_a_related_model() public function it_can_store_the_dirty_changes_when_updating_a_related_model() { $articleClass = new class() extends Article { - public static $logAttributes = ['name', 'text', 'user.name']; - - public static $logOnlyDirty = true; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['name', 'text', 'user.name'])->logOnlyDirty(); + } }; $user = User::create([ @@ -275,9 +407,13 @@ public function it_can_store_the_dirty_changes_when_updating_a_related_model() public function it_will_store_no_changes_when_not_logging_attributes() { $articleClass = new class() extends Article { - public static $logAttributes = []; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly([]); + } }; $article = new $articleClass(); @@ -297,7 +433,7 @@ public function it_will_store_the_values_when_deleting_the_model() $article->delete(); $expectedChanges = collect([ - 'attributes' => [ + 'old' => [ 'name' => 'my name', 'text' => null, ], @@ -311,9 +447,13 @@ public function it_will_store_the_values_when_deleting_the_model() public function it_will_store_the_values_when_deleting_the_model_with_softdeletes() { $articleClass = new class() extends Article { - public static $logAttributes = ['name', 'text']; use LogsActivity, SoftDeletes; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['name', 'text']); + } }; $article = new $articleClass(); @@ -323,7 +463,7 @@ public function it_will_store_the_values_when_deleting_the_model_with_softdelete $article->delete(); $expectedChanges = collect([ - 'attributes' => [ + 'old' => [ 'name' => 'my name', 'text' => null, ], @@ -335,7 +475,7 @@ public function it_will_store_the_values_when_deleting_the_model_with_softdelete $article->forceDelete(); $expectedChanges = collect([ - 'attributes' => [ + 'old' => [ 'name' => 'my name', 'text' => null ], @@ -352,11 +492,15 @@ public function it_will_store_the_values_when_deleting_the_model_with_softdelete public function it_can_store_the_changes_of_array_casted_properties() { $articleClass = new class() extends Article { - public static $logAttributes = ['json']; - public static $logOnlyDirty = true; + protected $casts = ['json' => 'collection']; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['json'])->logOnlyDirty(); + } }; $article = $articleClass::create([ @@ -386,9 +530,14 @@ public function it_can_use_nothing_as_loggable_attributes() { $articleClass = new class() extends Article { protected $fillable = ['name', 'text']; - protected static $logFillable = false; + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->dontLogFillable(); + } }; $article = new $articleClass(); @@ -406,10 +555,13 @@ public function it_can_use_text_as_loggable_attributes() { $articleClass = new class() extends Article { protected $fillable = ['name', 'text']; - protected static $logAttributes = ['text']; - protected static $logFillable = false; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['text'])->dontLogFillable(); + } }; $article = new $articleClass(); @@ -431,9 +583,13 @@ public function it_can_use_fillable_as_loggable_attributes() { $articleClass = new class() extends Article { protected $fillable = ['name', 'text']; - protected static $logFillable = true; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logFillable(); + } }; $article = new $articleClass(); @@ -455,10 +611,13 @@ public function it_can_use_both_fillable_and_log_attributes() { $articleClass = new class() extends Article { protected $fillable = ['name']; - protected static $logAttributes = ['text']; - protected static $logFillable = true; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['text'])->logFillable(); + } }; $article = new $articleClass(); @@ -480,9 +639,13 @@ public function it_can_use_both_fillable_and_log_attributes() public function it_can_use_wildcard_for_loggable_attributes() { $articleClass = new class() extends Article { - public static $logAttributes = ['*']; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['*']); + } }; $article = new $articleClass(); @@ -499,6 +662,8 @@ public function it_can_use_wildcard_for_loggable_attributes() 'id' => $article->id, 'user_id' => null, 'json' => null, + 'price' => null, + 'interval' => null, 'created_at' => $this->isLaravel6OrLower() ? '2017-01-01 12:00:00' : '2017-01-01T12:00:00.000000Z', 'updated_at' => $this->isLaravel6OrLower() ? '2017-01-01 12:00:00' : '2017-01-01T12:00:00.000000Z', ], @@ -511,9 +676,13 @@ public function it_can_use_wildcard_for_loggable_attributes() public function it_can_use_wildcard_with_relation() { $articleClass = new class() extends Article { - public static $logAttributes = ['*', 'user.name']; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['*', 'user.name']); + } }; $user = User::create([ @@ -536,6 +705,8 @@ public function it_can_use_wildcard_with_relation() 'deleted_at' => null, 'user_id' => $user->id, 'json' => null, + 'price' => null, + 'interval' => null, 'created_at' => $this->isLaravel6OrLower() ? '2017-01-01 12:00:00' : '2017-01-01T12:00:00.000000Z', 'updated_at' => $this->isLaravel6OrLower() ? '2017-01-01 12:00:00' : '2017-01-01T12:00:00.000000Z', 'user.name' => 'user name', @@ -549,10 +720,13 @@ public function it_can_use_wildcard_with_relation() public function it_can_use_wildcard_when_updating_model() { $articleClass = new class() extends Article { - public static $logAttributes = ['*']; - public static $logOnlyDirty = true; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['*'])->logOnlyDirty(); + } }; $user = User::create([ @@ -573,11 +747,11 @@ public function it_can_use_wildcard_when_updating_model() $expectedChanges = [ 'attributes' => [ 'name' => 'changed name', - 'updated_at' => $this->isLaravel6OrLower() ? '2018-01-01 12:00:00':'2018-01-01T12:00:00.000000Z', + 'updated_at' => $this->isLaravel6OrLower() ? '2018-01-01 12:00:00' : '2018-01-01T12:00:00.000000Z', ], 'old' => [ 'name' => 'article name', - 'updated_at' => $this->isLaravel6OrLower() ? '2017-01-01 12:00:00':'2017-01-01T12:00:00.000000Z', + 'updated_at' => $this->isLaravel6OrLower() ? '2017-01-01 12:00:00' : '2017-01-01T12:00:00.000000Z', ], ]; @@ -596,11 +770,15 @@ protected function createArticle(): Article protected function createDirtyArticle(): Article { $articleClass = new class() extends Article { - public static $logAttributes = ['name', 'text']; public static $logOnlyDirty = true; use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['name', 'text'])->logOnlyDirty(); + } }; $article = new $articleClass(); @@ -609,4 +787,1344 @@ protected function createDirtyArticle(): Article return $article; } + + /** @test */ + public function it_detect_changes_for_null_date_inteval_attributes() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'interval' => IntervalCasts::class, + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->dontLogIfAttributesChangedOnly(['created_at', 'updated_at', 'deleted_at']) + ->logOnly(['name', 'interval']); + } + }; + + $nullIntevalArticle = $articleClass::create([ + 'name' => 'Hamburg', + ]); + + $nullIntevalArticle->update(['name' => 'New name', 'interval' => CarbonInterval::month()]); + + $expectedChangesForNullInterval = [ + 'attributes' => [ + 'name' => 'New name', + 'interval' => '1 month', + ], + 'old' => [ + 'name' => 'Hamburg', + 'interval' => null, + ], + ]; + $this->assertEquals($expectedChangesForNullInterval, $this->getLastActivity()->changes()->toArray()); + + $intervalArticle = $articleClass::create([ + 'name' => 'Hamburg', + 'interval' => CarbonInterval::month(), + ]); + + $intervalArticle->update(['name' => 'New name', 'interval' => null]); + + $expectedChangesForInterval = [ + 'attributes' => [ + 'name' => 'New name', + 'interval' => null, + ], + 'old' => [ + 'name' => 'Hamburg', + 'interval' => '1 month', + ], + ]; + + $this->assertEquals($expectedChangesForInterval, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_retruns_same_uuid_for_all_log_changes_under_one_batch() + { + $articleClass = new class() extends Article { + use LogsActivity; + use SoftDeletes; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text']); + } + }; + + app(LogBatch::class)->startBatch(); + + $user = User::create([ + 'name' => 'user name', + ]); + + $article = $articleClass::create([ + 'name' => 'original name', + 'text' => 'original text', + 'user_id' => $user->id, + ]); + + $article->name = 'updated name'; + $article->text = 'updated text'; + $article->save(); + + $article->delete(); + $article->forceDelete(); + + $batchUuid = app(LogBatch::class)->getUuid(); + + app(LogBatch::class)->endBatch(); + + $this->assertTrue(Activity::pluck('batch_uuid')->every(fn($uuid) => $uuid === $batchUuid)); + } + + /** @test */ + public function it_assigns_new_uuid_for_multiple_change_logs_in_different_batches() + { + $articleClass = new class() extends Article { + use LogsActivity; + use SoftDeletes; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text']); + } + }; + + app(LogBatch::class)->startBatch(); + + $uuidForCreatedEvent = app(LogBatch::class)->getUuid(); + $user = User::create([ + 'name' => 'user name', + ]); + + $article = $articleClass::create([ + 'name' => 'original name', + 'text' => 'original text', + 'user_id' => $user->id, + ]); + + app(LogBatch::class)->endBatch(); + + $this->assertTrue(Activity::pluck('batch_uuid')->every(fn($uuid) => $uuid === $uuidForCreatedEvent)); + + app(LogBatch::class)->startBatch(); + + $article->name = 'updated name'; + $article->text = 'updated text'; + $article->save(); + $uuidForUpdatedEvents = app(LogBatch::class)->getUuid(); + + app(LogBatch::class)->endBatch(); + + $this->assertCount(1, Activity::where('description', 'updated')->get()); + + $this->assertEquals($uuidForUpdatedEvents, Activity::where('description', 'updated')->first()->batch_uuid); + + app(LogBatch::class)->startBatch(); + $article->delete(); + $article->forceDelete(); + + $uuidForDeletedEvents = app(LogBatch::class)->getUuid(); + + app(LogBatch::class)->endBatch(); + + $this->assertCount(2, Activity::where('batch_uuid', $uuidForDeletedEvents)->get()); + + $this->assertNotSame($uuidForCreatedEvent, $uuidForDeletedEvents); + } + + /** @test */ + public function it_can_removes_key_event_if_it_was_loggable() + { + $articleClass = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text', 'user.name']); + } + }; + + $user = User::create([ + 'name' => 'user name', + ]); + + $articleClass::addLogChange(new class() implements LoggablePipe { + public function handle(EventLogBag $event, \Closure $next): EventLogBag + { + Arr::forget($event->changes, ['attributes.name', 'old.name']); + + return $next($event); + } + }); + + $article = $articleClass::create([ + 'name' => 'original name', + 'text' => 'original text', + 'user_id' => $user->id, + ]); + + $article->name = 'updated name'; + $article->text = 'updated text'; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'text' => 'updated text', + 'user.name' => 'user name', + ], + 'old' => [ + 'text' => 'original text', + 'user.name' => 'user name', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_can_store_the_changes_when_updating_a_snake_case_related_model() + { + $articleClass = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text', 'snakeUser.name']); + } + + public function snake_user() + { + return $this->belongsTo(User::class, 'user_id'); + } + }; + + $user = User::create([ + 'name' => 'a name', + ]); + + $anotherUser = User::create([ + 'name' => 'another name', + ]); + + $article = $articleClass::create([ + 'name' => 'name', + 'text' => 'text', + 'user_id' => $user->id, + ]); + + $article->user()->associate($anotherUser)->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'name', + 'text' => 'text', + 'snake_user.name' => 'another name', + ], + 'old' => [ + 'name' => 'name', + 'text' => 'text', + 'snake_user.name' => 'a name', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_can_store_the_changes_when_updating_a_camel_case_related_model() + { + $articleClass = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text', 'camel_user.name']); + } + + public function camelUser() + { + return $this->belongsTo(User::class, 'user_id'); + } + }; + + $user = User::create([ + 'name' => 'a name', + ]); + + $anotherUser = User::create([ + 'name' => 'another name', + ]); + + $article = $articleClass::create([ + 'name' => 'name', + 'text' => 'text', + 'user_id' => $user->id, + ]); + + $article->user()->associate($anotherUser)->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'name', + 'text' => 'text', + 'camelUser.name' => 'another name', + ], + 'old' => [ + 'name' => 'name', + 'text' => 'text', + 'camelUser.name' => 'a name', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_can_store_the_changes_when_updating_a_custom_case_related_model() + { + $articleClass = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text', 'Custom_Case_User.name']); + } + + public function Custom_Case_User() + { + return $this->belongsTo(User::class, 'user_id'); + } + }; + + $user = User::create([ + 'name' => 'a name', + ]); + + $anotherUser = User::create([ + 'name' => 'another name', + ]); + + $article = $articleClass::create([ + 'name' => 'name', + 'text' => 'text', + 'user_id' => $user->id, + ]); + + $article->user()->associate($anotherUser)->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'name', + 'text' => 'text', + 'Custom_Case_User.name' => 'another name', + ], + 'old' => [ + 'name' => 'name', + 'text' => 'text', + 'Custom_Case_User.name' => 'a name', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_can_store_the_changes_when_saving_including_multi_level_related_model() + { + $articleClass = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text', 'user.latest_article.name']) + ->logOnlyDirty(); + } + }; + + $user = User::create([ + 'name' => 'a name', + ]); + + $articleClass::create([ + 'name' => 'name #1', + 'text' => 'text #1', + 'user_id' => $user->id, + ]); + + $articleClass::create([ + 'name' => 'name #2', + 'text' => 'text #2', + 'user_id' => $user->id, + ]); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'name #2', + 'text' => 'text #2', + 'user.latestArticle.name' => 'name #1', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_can_store_the_changes_of_collection_casted_properties() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = ['json' => 'collection']; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['json']) + ->logOnlyDirty(); + } + }; + + $article = $articleClass::create([ + 'json' => ['value' => 'original'], + ]); + + $article->json = collect(['value' => 'updated']); + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'json' => [ + 'value' => 'updated', + ], + ], + 'old' => [ + 'json' => [ + 'value' => 'original', + ], + ], + ]; + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_can_store_the_changes_of_json_casted_properties() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = ['json' => 'json']; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['json']) + ->logOnlyDirty(); + } + }; + + $article = $articleClass::create([ + 'json' => ['value' => 'original'], + ]); + + $article->json = collect(['value' => 'updated']); + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'json' => [ + 'value' => 'updated', + ], + ], + 'old' => [ + 'json' => [ + 'value' => 'original', + ], + ], + ]; + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_can_store_the_changes_when_a_boolean_field_is_changed_from_false_to_null() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'text' => 'boolean', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logAll() + ->logOnlyDirty(); + } + }; + + $user = User::create([ + 'name' => 'user name', + ]); + + Carbon::setTestNow(Carbon::create(2017, 1, 1, 12, 0, 0)); + $article = $articleClass::create([ + 'name' => 'article name', + 'text' => false, + 'user_id' => $user->id, + ]); + + $article->text = null; + Carbon::setTestNow(Carbon::create(2018, 1, 1, 12, 0, 0)); + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'text' => null, + 'updated_at' => '2018-01-01T12:00:00.000000Z', + + ], + 'old' => [ + 'text' => false, + 'updated_at' => '2017-01-01T12:00:00.000000Z', + ], + ]; + + $this->assertSame($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_can_use_ignored_attributes_while_updating() + { + $articleClass = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logAll() + ->logExcept(['name', 'updated_at']); + } + }; + + $article = new $articleClass(); + $article->name = 'my name'; + + Carbon::setTestNow(Carbon::create(2017, 1, 1, 12, 0, 0)); + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'text' => null, + 'deleted_at' => null, + 'id' => $article->id, + 'user_id' => null, + 'json' => null, + 'price' => null, + 'interval' => null, + 'created_at' => '2017-01-01T12:00:00.000000Z', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public function it_can_use_unguarded_as_loggable_attributes() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $guarded = ['text', 'json']; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logUnguarded() + ->logExcept(['id', 'created_at', 'updated_at', 'deleted_at']); + } + }; + + $article = new $articleClass(); + $article->name = 'my name'; + $article->text = 'my new text'; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'my name', + 'user_id' => null, + 'price' => null, + 'interval' => null, + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + + /** @test */ + public + function it_will_store_no_changes_when_wildcard_guard_and_log_unguarded_attributes() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $guarded = ['*']; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logUnguarded(); + } + }; + + $article = new $articleClass(); + $article->name = 'my name'; + $article->text = 'my new text'; + $article->save(); + + $this->assertEquals([], $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public + function it_can_use_hidden_as_loggable_attributes() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $hidden = ['text']; + protected $fillable = ['name', 'text']; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text']); + } + }; + + $article = new $articleClass(); + $article->name = 'my name'; + $article->text = 'my text'; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'my name', + 'text' => 'my text', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public + function it_can_use_overloaded_as_loggable_attributes() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $fillable = ['name', 'text']; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text', 'description']); + } + + public function setDescriptionAttribute($value) + { + $this->attributes['json'] = json_encode(['description' => $value]); + } + + public function getDescriptionAttribute() + { + return Arr::get(json_decode($this->attributes['json'], true), 'description'); + } + }; + + $article = new $articleClass(); + $article->name = 'my name'; + $article->text = 'my text'; + $article->description = 'my description'; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'my name', + 'text' => 'my text', + 'description' => 'my description', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public + function it_can_use_mutated_as_loggable_attributes() + { + $userClass = new class() extends User { + use LogsActivity; + + protected $fillable = ['name', 'text']; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logAll(); + } + + public function setNameAttribute($value) + { + $this->attributes['name'] = strtoupper($value); + } + }; + + Carbon::setTestNow(Carbon::create(2017, 1, 1, 12, 0, 0)); + $user = new $userClass(); + $user->name = 'my name'; + $user->text = 'my text'; + $user->save(); + + $expectedChanges = [ + 'attributes' => [ + 'id' => $user->id, + 'name' => 'MY NAME', + 'text' => 'my text', + 'created_at' => '2017-01-01T12:00:00.000000Z', + 'updated_at' => '2017-01-01T12:00:00.000000Z', + 'deleted_at' => null, + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + + $user->name = 'my name 2'; + $user->save(); + + $expectedChanges = [ + 'old' => [ + 'id' => $user->id, + 'name' => 'MY NAME', + 'text' => 'my text', + 'created_at' => '2017-01-01T12:00:00.000000Z', + 'updated_at' => '2017-01-01T12:00:00.000000Z', + 'deleted_at' => null, + ], + 'attributes' => [ + 'id' => $user->id, + 'name' => 'MY NAME 2', + 'text' => 'my text', + 'created_at' => '2017-01-01T12:00:00.000000Z', + 'updated_at' => '2017-01-01T12:00:00.000000Z', + 'deleted_at' => null, + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public + function it_can_use_accessor_as_loggable_attributes() + { + $userClass = new class() extends User { + use LogsActivity; + + protected $fillable = ['name', 'text']; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logAll(); + } + + public function getNameAttribute($value) + { + return strtoupper($value); + } + }; + + Carbon::setTestNow(Carbon::create(2017, 1, 1, 12, 0, 0)); + $user = new $userClass(); + $user->name = 'my name'; + $user->text = 'my text'; + $user->save(); + + $expectedChanges = [ + 'attributes' => [ + 'id' => $user->id, + 'name' => 'MY NAME', + 'text' => 'my text', + 'created_at' => '2017-01-01T12:00:00.000000Z', + 'updated_at' => '2017-01-01T12:00:00.000000Z', + 'deleted_at' => null, + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + + $user->name = 'my name 2'; + $user->save(); + + $expectedChanges = [ + 'old' => [ + 'id' => $user->id, + 'name' => 'MY NAME', + 'text' => 'my text', + 'created_at' => '2017-01-01T12:00:00.000000Z', + 'updated_at' => '2017-01-01T12:00:00.000000Z', + 'deleted_at' => null, + ], + 'attributes' => [ + 'id' => $user->id, + 'name' => 'MY NAME 2', + 'text' => 'my text', + 'created_at' => '2017-01-01T12:00:00.000000Z', + 'updated_at' => '2017-01-01T12:00:00.000000Z', + 'deleted_at' => null, + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public + function it_can_use_encrypted_as_loggable_attributes() + { + $userClass = new class() extends User { + use LogsActivity; + + protected $fillable = ['name', 'text']; + protected $encryptable = ['name', 'text']; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text']); + } + + public function getAttributeValue($key) + { + $value = parent::getAttributeValue($key); + + if (in_array($key, $this->encryptable)) { + $value = decrypt($value); + } + + return $value; + } + + public function setAttribute($key, $value) + { + if (in_array($key, $this->encryptable)) { + $value = encrypt($value); + } + + return parent::setAttribute($key, $value); + } + }; + + Carbon::setTestNow(Carbon::create(2017, 1, 1, 12, 0, 0)); + $user = new $userClass(); + $user->name = 'my name'; + $user->text = 'my text'; + $user->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'my name', + 'text' => 'my text', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + + $user->name = 'my name 2'; + $user->save(); + + $expectedChanges = [ + 'old' => [ + 'name' => 'my name', + 'text' => 'my text', + ], + 'attributes' => [ + 'name' => 'my name 2', + 'text' => 'my text', + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public + function it_can_use_casted_as_loggable_attribute() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'price' => 'float', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'text', 'price']) + ->logOnlyDirty(); + } + }; + + $article = new $articleClass(); + $article->name = 'my name'; + $article->text = 'my text'; + $article->price = '9.99'; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'my name', + 'text' => 'my text', + 'price' => 9.99, + ], + ]; + + $changes = $this->getLastActivity()->changes()->toArray(); + $this->assertSame($expectedChanges, $changes); + $this->assertIsFloat($changes['attributes']['price']); + + $article->price = 19.99; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'price' => 19.99, + ], + 'old' => [ + 'price' => 9.99, + ], + ]; + + $changes = $this->getLastActivity()->changes()->toArray(); + $this->assertSame($expectedChanges, $changes); + $this->assertIsFloat($changes['attributes']['price']); + } + + /** @test */ + public + function it_can_use_nullable_date_as_loggable_attributes() + { + $userClass = new class() extends User { + use LogsActivity; + use SoftDeletes; + + protected $fillable = ['name', 'text']; + + protected $dates = [ + 'created_at', + 'updated_at', + 'deleted_at', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logAll(); + } + }; + + Carbon::setTestNow(Carbon::create(2017, 1, 1, 12, 0, 0)); + $user = new $userClass(); + $user->name = 'my name'; + $user->text = 'my text'; + $user->save(); + + $expectedChanges = [ + 'attributes' => [ + 'id' => $user->getKey(), + 'name' => 'my name', + 'text' => 'my text', + 'created_at' => '2017-01-01T12:00:00.000000Z', + 'updated_at' => '2017-01-01T12:00:00.000000Z', + 'deleted_at' => null, + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public + function it_can_use_custom_date_cast_as_loggable_attributes() + { + $userClass = new class() extends User { + use LogsActivity; + + protected $fillable = ['name', 'text']; + protected $casts = [ + 'created_at' => 'date:d.m.Y', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logAll(); + } + }; + + Carbon::setTestNow(Carbon::create(2017, 1, 1, 12, 0, 0)); + $user = new $userClass(); + $user->name = 'my name'; + $user->text = 'my text'; + $user->save(); + + $expectedChanges = [ + 'attributes' => [ + 'id' => $user->getKey(), + 'name' => 'my name', + 'text' => 'my text', + 'created_at' => '01.01.2017', + 'updated_at' => '2017-01-01T12:00:00.000000Z', + 'deleted_at' => null, + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public + function it_can_use_custom_immutable_date_cast_as_loggable_attributes() + { + $userClass = new class() extends User { + use LogsActivity; + + protected $fillable = ['name', 'text']; + protected $casts = [ + 'created_at' => 'immutable_date:d.m.Y', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logAll(); + } + }; + + Carbon::setTestNow(Carbon::create(2017, 1, 1, 12, 0, 0)); + $user = new $userClass(); + $user->name = 'my name'; + $user->text = 'my text'; + $user->save(); + + $expectedChanges = [ + 'attributes' => [ + 'id' => $user->getKey(), + 'name' => 'my name', + 'text' => 'my text', + 'created_at' => '01.01.2017', + 'updated_at' => '2017-01-01T12:00:00.000000Z', + 'deleted_at' => null, + ], + ]; + + $this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray()); + } + + /** @test */ + public + function it_can_store_the_changes_of_json_attributes() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'json' => 'collection', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'json->data']) + ->logOnlyDirty(); + } + }; + + $article = new $articleClass(); + $article->json = ['data' => 'test']; + $article->name = 'I am JSON'; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'I am JSON', + 'json' => [ + 'data' => 'test', + ], + ], + ]; + + $changes = $this->getLastActivity()->changes()->toArray(); + + $this->assertSame($expectedChanges, $changes); + } + + /** @test */ + public + function it_will_not_store_changes_to_untracked_json() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'json' => 'collection', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'json->data']) + ->logOnlyDirty(); + } + }; + + $article = new $articleClass(); + $article->json = ['unTracked' => 'test']; + $article->name = 'a name'; + $article->save(); + + $article->name = 'I am JSON'; + $article->json = ['unTracked' => 'different string']; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'name' => 'I am JSON', + ], + 'old' => [ + 'name' => 'a name', + ], + ]; + + $changes = $this->getLastActivity()->changes()->toArray(); + + $this->assertSame($expectedChanges, $changes); + } + + /** @test */ + public + function it_will_return_null_for_missing_json_attribute() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'json' => 'collection', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'json->data->missing']) + ->logOnlyDirty(); + } + }; + + $jsonToStore = []; + + $article = new $articleClass(); + $article->json = $jsonToStore; + $article->name = 'I am JSON'; + $article->save(); + + data_set($jsonToStore, 'data.missing', 'I wasn\'t here'); + + $article->json = $jsonToStore; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'json' => [ + 'data' => [ + 'missing' => 'I wasn\'t here', + ], + ], + ], + 'old' => [ + 'json' => [ + 'data' => [ + 'missing' => null, + ], + ], + ], + ]; + + $changes = $this->getLastActivity()->changes()->toArray(); + + $this->assertSame($expectedChanges, $changes); + } + + /** @test */ + public + function it_will_return_an_array_for_sub_key_in_json_attribute() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'json' => 'collection', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'json->data']) + ->logOnlyDirty(); + } + }; + + $jsonToStore = [ + 'data' => [ + 'data_a' => 1, + 'data_b' => 2, + 'data_c' => 3, + 'data_d' => 4, + 'data_e' => 5, + ], + ]; + + $article = new $articleClass(); + $article->json = $jsonToStore; + $article->name = 'I am JSON'; + $article->save(); + + data_set($jsonToStore, 'data.data_c', 'I Got The Key'); + + $article->json = $jsonToStore; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'json' => [ + 'data' => [ + 'data_a' => 1, + 'data_b' => 2, + 'data_c' => 'I Got The Key', + 'data_d' => 4, + 'data_e' => 5, + ], + ], + ], + 'old' => [ + 'json' => [ + 'data' => [ + 'data_a' => 1, + 'data_b' => 2, + 'data_c' => 3, + 'data_d' => 4, + 'data_e' => 5, + ], + ], + ], + ]; + + $changes = $this->getLastActivity()->changes()->toArray(); + + $this->assertSame($expectedChanges, $changes); + } + + /** @test */ + public + function it_will_access_further_than_level_one_json_attribute() + { + $articleClass = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'json' => 'collection', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'json->data->can->go->how->far']) + ->logOnlyDirty(); + } + }; + + $jsonToStore = []; + // data_set($jsonToStore, 'data.can.go.how.far', 'Data'); + + $article = new $articleClass(); + $article->json = $jsonToStore; + $article->name = 'I am JSON'; + $article->save(); + + data_set($jsonToStore, 'data.can.go.how.far', 'This far'); + + $article->json = $jsonToStore; + $article->save(); + + $expectedChanges = [ + 'attributes' => [ + 'json' => [ + 'data' => [ + 'can' => [ + 'go' => [ + 'how' => [ + 'far' => 'This far', + ], + ], + ], + ], + ], + ], + 'old' => [ + 'json' => [ + 'data' => [ + 'can' => [ + 'go' => [ + 'how' => [ + 'far' => null, + ], + ], + ], + ], + ], + ], + ]; + + $changes = $this->getLastActivity()->changes()->toArray(); + + $this->assertSame($expectedChanges, $changes); + } } diff --git a/tests/HasActivityTest.php b/tests/HasActivityTest.php index 71c5fb7..2fc301e 100644 --- a/tests/HasActivityTest.php +++ b/tests/HasActivityTest.php @@ -6,6 +6,7 @@ use Padosoft\Laravel\ActivitylogExtended\Test\Models\User; use Illuminate\Database\Eloquent\SoftDeletes; use Padosoft\Laravel\ActivitylogExtended\Traits\LogsActivityWithRelations; +use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\CausesActivity; use Spatie\Activitylog\Traits\LogsActivity; @@ -13,7 +14,7 @@ class HasActivityTest extends TestCase { protected $user; - public function setUp():void + public function setUp(): void { parent::setUp(); @@ -21,6 +22,11 @@ public function setUp():void use LogsActivity; use CausesActivity; use SoftDeletes; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults(); + } }; $this->assertCount(0, Activity::all()); diff --git a/tests/LogBatchTest.php b/tests/LogBatchTest.php new file mode 100644 index 0000000..243d232 --- /dev/null +++ b/tests/LogBatchTest.php @@ -0,0 +1,160 @@ +activityDescription = 'My activity'; + parent::setUp(); + + collect(range(1, 5))->each(function (int $index) { + $logName = "log{$index}"; + activity($logName)->log('hello everybody'); + }); + } + + /** @test */ + public function it_generates_uuid_after_start_and_end_batch_properely() + { + LogBatch::startBatch(); + $uuid = LogBatch::getUuid(); + LogBatch::endBatch(); + + $this->assertFalse(LogBatch::isopen()); + + $this->assertIsString($uuid); + } + + /** @test */ + public function it_returns_null_uuid_after_end_batch_properely() + { + LogBatch::startBatch(); + $uuid = LogBatch::getUuid(); + LogBatch::endBatch(); + + $this->assertFalse(LogBatch::isopen()); + $this->assertNotNull($uuid); + $this->assertNull(LogBatch::getUuid()); + } + + /** @test */ + public function it_generates_a_new_uuid_after_starting_new_batch_properly() + { + LogBatch::startBatch(); + $firstBatchUuid = LogBatch::getUuid(); + LogBatch::endBatch(); + + LogBatch::startBatch(); + + LogBatch::startBatch(); + $secondBatchUuid = LogBatch::getUuid(); + LogBatch::endBatch(); + + $this->assertTrue(LogBatch::isopen()); + $this->assertNotNull($firstBatchUuid); + $this->assertNotNull($secondBatchUuid); + + $this->assertNotEquals($firstBatchUuid, $secondBatchUuid); + } + + /** @test */ + public function it_will_not_generate_new_uuid_if_start_already_started_batch() + { + LogBatch::startBatch(); + + $firstUuid = LogBatch::getUuid(); + + LogBatch::startBatch(); + + $secondUuid = LogBatch::getUuid(); + + LogBatch::endBatch(); + + $this->assertTrue(LogBatch::isopen()); + + $this->assertEquals($secondUuid, $firstUuid); + } + + /** @test */ + public function it_will_not_generate_uuid_if_end_batch_before_starting() + { + LogBatch::endBatch(); + $uuid = LogBatch::getUuid(); + + LogBatch::startBatch(); + + $this->assertNull($uuid); + } + + /** @test */ + public function it_can_set_uuid_and_start_a_batch() + { + $uuid = Str::uuid(); + + LogBatch::setBatch($uuid); + $this->assertTrue(LogBatch::isOpen()); + $this->assertEquals($uuid, LogBatch::getUuid()); + + LogBatch::endBatch(); + $this->assertFalse(LogBatch::isOpen()); + } + + /** @test */ + public function it_can_set_uuid_for_already_started_batch() + { + $uuid = Str::uuid(); + + LogBatch::startBatch(); + $this->assertTrue(LogBatch::isOpen()); + $this->assertNotEquals($uuid, LogBatch::getUuid()); + + LogBatch::setBatch($uuid); + $this->assertTrue(LogBatch::isOpen()); + $this->assertEquals($uuid, LogBatch::getUuid()); + + LogBatch::endBatch(); + $this->assertFalse(LogBatch::isOpen()); + } + + /** @test */ + public function it_will_not_return_null_uuid_if_end_batch_that_started_twice() + { + LogBatch::startBatch(); + $firstUuid = LogBatch::getUuid(); + + LogBatch::startBatch(); + + LogBatch::endBatch(); + + $notNullUuid = LogBatch::getUuid(); + + $this->assertNotNull($firstUuid); + $this->assertNotNull($notNullUuid); + + $this->assertEquals($notNullUuid, $firstUuid); + } + + /** @test */ + public function it_will_return_null_uuid_if_end_batch_that_started_twice_properly() + { + LogBatch::startBatch(); + $firstUuid = LogBatch::getUuid(); + + LogBatch::startBatch(); + + LogBatch::endBatch(); + LogBatch::endBatch(); + + $nullUuid = LogBatch::getUuid(); + + $this->assertNotNull($firstUuid); + $this->assertNull($nullUuid); + + $this->assertNotSame($firstUuid, $nullUuid); + } +} diff --git a/tests/LogsActivityTest.php b/tests/LogsActivityTest.php index 45dd439..90219a2 100644 --- a/tests/LogsActivityTest.php +++ b/tests/LogsActivityTest.php @@ -2,8 +2,14 @@ namespace Padosoft\Laravel\ActivitylogExtended\Test; +use Carbon\Carbon; +use Illuminate\Support\Collection; use Padosoft\Laravel\ActivitylogExtended\Models\Activity; use Padosoft\Laravel\ActivitylogExtended\Test\Models\Article; +use Padosoft\Laravel\ActivitylogExtended\Test\Models\Issue733; +use Padosoft\Laravel\ActivitylogExtended\Test\Models\User; +use Spatie\Activitylog\Contracts\Activity as ActivityContract; +use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\LogsActivity; use Illuminate\Database\Eloquent\SoftDeletes; @@ -19,6 +25,21 @@ public function setUp(): void $this->article = new class() extends Article { use LogsActivity; use SoftDeletes; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults(); + } + }; + + $this->user = new class() extends User { + use LogsActivity; + use SoftDeletes; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults(); + } }; $this->assertCount(0, Activity::all()); @@ -96,6 +117,11 @@ public function it_will_log_the_deletion_of_a_model_without_softdeletes() { $articleClass = new class() extends Article { use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults(); + } }; $article = new $articleClass(); @@ -159,6 +185,26 @@ public function it_can_fetch_all_activity_for_a_model() $this->assertCount(2, $activities); } + /** @test */ + public function it_can_log_activity_to_log_named_in_the_model() + { + $articleClass = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->useLogName('custom_log'); + } + }; + + $article = new $articleClass(); + $article->name = 'my name'; + $article->save(); + + $this->assertSame('custom_log', Activity::latest()->first()->log_name); + } + /** @test */ public function it_can_fetch_soft_deleted_models() { @@ -187,9 +233,9 @@ public function it_can_log_activity_to_log_returned_from_model_method_override() $articleClass = new class() extends Article { use LogsActivity; - public function getLogNameToUse() + public function getActivitylogOptions(): LogOptions { - return 'custom_log'; + return LogOptions::defaults()->useLogName('custom_log'); } }; @@ -202,42 +248,258 @@ public function getLogNameToUse() } /** @test */ - public function it_can_log_activity_to_log_named_in_the_model() + public function it_will_not_log_an_update_of_the_model_if_only_ignored_attributes_are_changed() { $articleClass = new class() extends Article { use LogsActivity; - protected static $logName = 'custom_log'; + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->dontLogIfAttributesChangedOnly(['text']); + } }; $article = new $articleClass(); $article->name = 'my name'; $article->save(); - $this->assertSame('custom_log', Activity::latest()->first()->log_name); + $article->text = 'ignore me'; + $article->save(); + + $this->assertCount(1, Activity::all()); + + $this->assertInstanceOf(get_class($articleClass), $this->getLastActivity()->subject); + $this->assertEquals($article->id, $this->getLastActivity()->subject->id); + $this->assertEquals('created', $this->getLastActivity()->description); } /** @test */ - public function it_will_not_log_an_update_of_the_model_if_only_ignored_attributes_are_changed() + public function can_log_activity_on_subject_by_same_causer() + { + $user = $this->loginWithFakeUser(); + + $user->name = 'LogsActivity Name'; + $user->save(); + + $this->assertCount(1, Activity::all()); + + $this->assertInstanceOf(get_class($this->user), $this->getLastActivity()->subject); + $this->assertEquals($user->id, $this->getLastActivity()->subject->id); + $this->assertEquals($user->id, $this->getLastActivity()->causer->id); + $this->assertCount(1, $user->activities); + $this->assertCount(1, $user->actions); + } + + /** @test */ + public function it_can_log_activity_when_attributes_are_changed_with_tap() + { + $model = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults(); + } + + protected $properties = [ + 'property' => [ + 'subProperty' => 'value', + ], + ]; + + public function tapActivity(ActivityContract $activity, string $eventName) + { + $properties = $this->properties; + $properties['event'] = $eventName; + $activity->properties = collect($properties); + $activity->created_at = Carbon::yesterday()->startOfDay(); + } + }; + + $entity = new $model(); + $entity->save(); + + $firstActivity = $entity->activities()->first(); + + $this->assertInstanceOf(Collection::class, $firstActivity->properties); + $this->assertEquals('value', $firstActivity->getExtraProperty('property.subProperty')); + $this->assertEquals('created', $firstActivity->description); + $this->assertEquals('created', $firstActivity->event); + $this->assertEquals(Carbon::yesterday()->startOfDay()->format('Y-m-d H:i:s'), $firstActivity->created_at->format('Y-m-d H:i:s')); + } + + /** @test */ + public function it_can_log_activity_when_description_is_changed_with_tap() + { + $model = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults(); + } + + public function tapActivity(ActivityContract $activity, string $eventName) + { + $activity->description = 'my custom description'; + } + }; + + $entity = new $model(); + $entity->save(); + + $firstActivity = $entity->activities()->first(); + + $this->assertEquals('my custom description', $firstActivity->description); + } + + /** @test */ + public function it_can_log_activity_when_event_is_changed_with_tap() + { + $model = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults(); + } + + public function tapActivity(ActivityContract $activity, string $eventName) + { + $activity->event = 'my custom event'; + } + }; + + $entity = new $model(); + $entity->save(); + + $firstActivity = $entity->activities()->first(); + + $this->assertEquals('my custom event', $firstActivity->event); + } + + /** @test */ + public function it_will_not_submit_log_when_there_is_no_changes() + { + $model = new class() extends Article { + use LogsActivity; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['text']) + ->dontSubmitEmptyLogs() + ->logOnlyDirty(); + } + }; + + $entity = new $model(['text' => 'test']); + $entity->save(); + + $this->assertCount(1, Activity::all()); + + $entity->name = 'my name'; + $entity->save(); + + $this->assertCount(1, Activity::all()); + } + + /** @test */ + public function it_will_submit_a_log_with_json_changes() + { + $model = new class() extends Article { + use LogsActivity; + + protected $casts = [ + 'json' => 'collection', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['text', 'json->data']) + ->dontSubmitEmptyLogs() + ->logOnlyDirty(); + } + }; + + $entity = new $model([ + 'text' => 'test', + 'json' => [ + 'data' => 'oldish', + ], + ]); + + $entity->save(); + + $this->assertCount(1, Activity::all()); + + $entity->json = [ + 'data' => 'chips', + 'irrelevant' => 'should not be', + ]; + + $entity->save(); + + $expectedChanges = [ + 'attributes' => [ + 'json' => [ + 'data' => 'chips', + ], + ], + 'old' => [ + 'json' => [ + 'data' => 'oldish', + ], + ], + ]; + + $changes = $this->getLastActivity()->changes()->toArray(); + + $this->assertCount(2, Activity::all()); + $this->assertSame($expectedChanges, $changes); + } + + /** @test */ + public function it_will_log_the_retrieval_of_the_model() + { + $article = Issue733::create(['name' => 'my name']); + + $retrieved = Issue733::whereKey($article->getKey())->first(); + $this->assertTrue($article->is($retrieved)); + + $activity = $this->getLastActivity(); + + $this->assertInstanceOf(get_class($article), $activity->subject); + $this->assertTrue($article->is($activity->subject)); + $this->assertEquals('retrieved', $activity->description); + } + + /** @test */ + public function it_will_not_log_casted_attribute_of_the_model_if_attribute_raw_values_is_used() { $articleClass = new class() extends Article { use LogsActivity; - protected static $ignoreChangedAttributes = ['text']; + protected $casts = [ + 'name' => 'encrypted', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults()->logOnly(['name'])->useAttributeRawValues(['name']); + } }; $article = new $articleClass(); $article->name = 'my name'; $article->save(); - $article->text = 'ignore me'; - $article->save(); - - $this->assertCount(1, Activity::all()); - $this->assertInstanceOf(get_class($articleClass), $this->getLastActivity()->subject); $this->assertEquals($article->id, $this->getLastActivity()->subject->id); + $this->assertNotEquals($article->name, $this->getLastActivity()->properties['attributes']['name']); $this->assertEquals('created', $this->getLastActivity()->description); + $this->assertEquals('created', $this->getLastActivity()->event); } /** @test */ @@ -247,10 +509,13 @@ public function it_will_not_fail_if_asked_to_replace_from_empty_attribute() use LogsActivity; use SoftDeletes; - public function getDescriptionForEvent(string $eventName): string + public function getActivitylogOptions(): LogOptions { - return ":causer.name $eventName"; + return LogOptions::defaults()->setDescriptionForEvent(function (string $eventName): string { + return ":causer.name $eventName"; + }); } + }; $entity = new $model(); diff --git a/tests/Models/ArticleWithRelations.php b/tests/Models/ArticleWithRelations.php index 5ff2c56..aa0e55e 100644 --- a/tests/Models/ArticleWithRelations.php +++ b/tests/Models/ArticleWithRelations.php @@ -3,15 +3,17 @@ namespace Padosoft\Laravel\ActivitylogExtended\Test\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Log; use Padosoft\Laravel\ActivitylogExtended\Traits\LogsActivityWithRelations; +use Spatie\Activitylog\LogOptions; class ArticleWithRelations extends Model { use LogsActivityWithRelations; - protected static $logAttributes = ['*']; + protected static $logAttributes = ['*']; protected static $ignoreChangedAttributes = ['updated_at']; - protected static $logOnlyDirty = true; + protected static $logOnlyDirty = true; protected $table = 'articles'; @@ -21,4 +23,9 @@ public function User() { return $this->belongsTo(User::class); } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults(); + } } diff --git a/tests/Models/CustomActivityModel.php b/tests/Models/CustomActivityModel.php index 81e2c0a..b67a57f 100644 --- a/tests/Models/CustomActivityModel.php +++ b/tests/Models/CustomActivityModel.php @@ -2,6 +2,7 @@ namespace Padosoft\Laravel\ActivitylogExtended\Test\Models; +use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; @@ -46,14 +47,14 @@ public function causer(): MorphTo * * @return mixed */ - public function getExtraProperty(string $propertyName) + public function getExtraProperty(string $propertyName): mixed { - return array_get($this->properties->toArray(), $propertyName); + return Arr::get($this->properties->toArray(), $propertyName); } public function changes(): Collection { - if (! $this->properties instanceof Collection) { + if (!$this->properties instanceof Collection) { return new Collection(); } diff --git a/tests/Models/CustomDatabaseConnectionOnActivityModel.php b/tests/Models/CustomDatabaseConnectionOnActivityModel.php new file mode 100644 index 0000000..c877fd8 --- /dev/null +++ b/tests/Models/CustomDatabaseConnectionOnActivityModel.php @@ -0,0 +1,10 @@ +properties->toArray(), $propertyName); + return Arr::get($this->properties->toArray(), $propertyName); } public function changes(): Collection { - if (! $this->properties instanceof Collection) { + if (!$this->properties instanceof Collection) { return new Collection(); } diff --git a/tests/Models/CustomTableNameOnActivityModel.php b/tests/Models/CustomTableNameOnActivityModel.php new file mode 100644 index 0000000..0b40435 --- /dev/null +++ b/tests/Models/CustomTableNameOnActivityModel.php @@ -0,0 +1,10 @@ +dontSubmitEmptyLogs() + ->logOnly(['name']); + } +} diff --git a/tests/Models/User.php b/tests/Models/User.php index ad5acd9..311ca4c 100644 --- a/tests/Models/User.php +++ b/tests/Models/User.php @@ -81,4 +81,9 @@ public function articles() { return $this->hasMany(Article::class); } + + public function latestArticle() + { + return $this->hasOne(Article::class)->latest()->limit(1); + } } diff --git a/tests/Models/UserWithRelations.php b/tests/Models/UserWithRelations.php index cc3e404..6607500 100644 --- a/tests/Models/UserWithRelations.php +++ b/tests/Models/UserWithRelations.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Model; use Padosoft\Laravel\ActivitylogExtended\Traits\LogsActivityWithRelations; +use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\CausesActivity; use Illuminate\Contracts\Auth\Authenticatable; @@ -83,4 +84,9 @@ public function articles() { return $this->hasMany(Article::class); } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults(); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 725a3ca..68e442d 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,6 +2,7 @@ namespace Padosoft\Laravel\ActivitylogExtended\Test; +use Illuminate\Support\Arr; use Padosoft\Laravel\ActivitylogExtended\Models\Activity; use Padosoft\Laravel\ActivitylogExtended\Test\Models\User; use Illuminate\Database\Schema\Blueprint; @@ -21,10 +22,10 @@ public function setUp(): void protected function checkRequirements() { - parent::checkRequirements(); + //parent::checkRequirements(); collect($this->getAnnotations())->filter(function ($location) { - return in_array('!Travis', array_get($location, 'requires', [])); + return in_array('!Travis', Arr::get($location, 'requires', [])); })->each(function ($location) { getenv('TRAVIS') && $this->markTestSkipped('Travis will not run this test.'); }); @@ -43,7 +44,7 @@ public function getEnvironmentSetUp($app) $app['config']->set('database.connections.sqlite', [ 'driver' => 'sqlite', - 'database' => $this->getTempDirectory().'/database.sqlite', + 'database' => $this->getTempDirectory() . '/database.sqlite', 'prefix' => '', ]); @@ -59,35 +60,37 @@ protected function setUpDatabase() $this->createActivityLogTable(); $this->createTables('articles', 'users'); - $this->seedModels(User::class,Article::class); + $this->seedModels(User::class, Article::class); } protected function resetDatabase() { - file_put_contents($this->getTempDirectory().'/database.sqlite', null); + file_put_contents($this->getTempDirectory() . '/database.sqlite', null); } protected function createActivityLogTable() { - include_once '__DIR__'.'/../vendor/spatie/laravel-activitylog/migrations/create_activity_log_table.php.stub'; + include_once __DIR__ . '/../vendor/spatie/laravel-activitylog/database/migrations/create_activity_log_table.php.stub'; (new \CreateActivityLogTable())->up(); - if (file_exists('__DIR__'.'/../vendor/spatie/laravel-activitylog/migrations/add_event_column_to_activity_log_table.php.stub')){ - include_once '__DIR__'.'/../vendor/spatie/laravel-activitylog/migrations/add_event_column_to_activity_log_table.php.stub'; + if (file_exists(__DIR__ . '/../vendor/spatie/laravel-activitylog/database/migrations/add_event_column_to_activity_log_table.php.stub')) { + include_once __DIR__ . '/../vendor/spatie/laravel-activitylog/database/migrations/add_event_column_to_activity_log_table.php.stub'; (new \AddEventColumnToActivityLogTable())->up(); } + if (file_exists(__DIR__ . '/../vendor/spatie/laravel-activitylog/database/migrations/add_batch_uuid_column_to_activity_log_table.php.stub')) { + include_once __DIR__ . '/../vendor/spatie/laravel-activitylog/database/migrations/add_batch_uuid_column_to_activity_log_table.php.stub'; + (new \AddBatchUuidColumnToActivityLogTable())->up(); + } - - - include_once '__DIR__'.'/../migrations/enhance_activity_log_table.php.stub'; + include_once __DIR__ . '/../migrations/enhance_activity_log_table.php.stub'; (new \EnhanceActivityLogTable())->up(); } public function getTempDirectory(): string { - return __DIR__.'/temp'; + return __DIR__ . '/temp'; } protected function createTables(...$tableNames) @@ -104,6 +107,8 @@ protected function createTables(...$tableNames) $table->integer('user_id')->unsigned()->nullable(); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->text('json')->nullable(); + $table->string('interval')->nullable(); + $table->decimal('price')->nullable(); } }); }); @@ -113,10 +118,10 @@ protected function seedModels(...$modelClasses) { collect($modelClasses)->each(function (string $modelClass) { foreach (range(1, 0) as $index) { - if ($modelClass==Article::class){ + if ($modelClass == Article::class) { $causer = User::first(); - $modelClass::create(['name' => "name {$index}",'user_id'=>$causer->id]); - }else { + $modelClass::create(['name' => "name {$index}", 'user_id' => $causer->id]); + } else { $modelClass::create(['name' => "name {$index}"]); } } @@ -138,8 +143,19 @@ public function doNotMarkAsRisky() public function isLaravel6OrLower(): bool { - $majorVersion = (int) substr(App::version(), 0, 1); + $majorVersion = (int)substr(App::version(), 0, 1); return $majorVersion <= 6; } + + public function loginWithFakeUser() + { + $user = new $this->user(); + + $user::find(1); + + $this->be($user); + + return $user; + } }