diff --git a/.travis.yml b/.travis.yml index 132d4a6..3bbeda9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,10 @@ language: php php: - - 5.4 - - 5.5 - 5.6 - 7.0 - 7.1 - -sudo: false + - 7.2 env: matrix: @@ -22,17 +19,22 @@ matrix: fast_finish: true include: - - php: 7.0 + - php: 7.1 env: PHPCS=1 DEFAULT=0 - - php: 7.0 + - php: 7.1 env: PHPSTAN=1 DEFAULT=0 + - php: 5.6 + env: PREFER_LOWEST=1 + before_script: - - if [[ $TRAVIS_PHP_VERSION != 7.0 ]]; then phpenv config-rm xdebug.ini; fi + - if [[ $TRAVIS_PHP_VERSION != 7.1 ]]; then phpenv config-rm xdebug.ini; fi - - composer self-update - - composer install --prefer-dist --no-interaction + - if [[ $PREFER_LOWEST != 1 ]]; then composer update --no-interaction ; fi + - if [[ $PREFER_LOWEST == 1 ]]; then composer update --no-interaction --prefer-lowest --prefer-stable; fi + + - if [[ $PHPSTAN = 1 ]]; then composer require phpstan/phpstan; fi - if [[ $DB = 'mysql' ]]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi - if [[ $DB = 'pgsql' ]]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi @@ -41,15 +43,14 @@ before_script: - if [[ $PHPSTAN = 1 ]]; then composer require phpstan/phpstan; fi script: - - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION != 7.0 ]]; then vendor/bin/phpunit; fi - - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.0 ]]; then vendor/bin/phpunit --coverage-clover=clover.xml; fi + - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION != 7.1 ]]; then vendor/bin/phpunit; fi + - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.1 ]]; then vendor/bin/phpunit --coverage-clover=clover.xml; fi - if [[ $PHPCS = 1 ]]; then vendor/bin/phpcs -n -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests; fi - - if [[ $PHPSTAN = 1 ]]; then vendor/bin/phpstan analyse -c phpstan.neon -l 5 src; fi after_success: - - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.0 ]]; then bash <(curl -s https://codecov.io/bash); fi + - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.1 ]]; then bash <(curl -s https://codecov.io/bash); fi notifications: email: false diff --git a/composer.json b/composer.json index 8379c4f..ee534b4 100644 --- a/composer.json +++ b/composer.json @@ -31,11 +31,12 @@ "source": "https://github.com/usemuffin/trash" }, "require": { - "cakephp/orm": "^3.0" + "cakephp/orm": "^3.5" }, "require-dev": { - "phpunit/phpunit": "<6.0", - "cakephp/cakephp": "^3.0" + "cakephp/chronos": "^1.1", + "cakephp/cakephp": "^3.5", + "phpunit/phpunit": "^5.7.14|^6.0" }, "autoload": { "psr-4": { diff --git a/src/Model/Behavior/TrashBehavior.php b/src/Model/Behavior/TrashBehavior.php index d53f372..1b18550 100644 --- a/src/Model/Behavior/TrashBehavior.php +++ b/src/Model/Behavior/TrashBehavior.php @@ -48,7 +48,7 @@ class TrashBehavior extends Behavior public function initialize(array $config) { if (!empty($config['events'])) { - $this->config('events', $config['events'], false); + $this->setConfig('events', $config['events'], false); } } @@ -61,10 +61,10 @@ public function initialize(array $config) public function implementedEvents() { $events = []; - if ($this->config('events') === false) { + if ($this->getConfig('events') === false) { return $events; } - foreach ((array)$this->config('events') as $eventKey => $event) { + foreach ((array)$this->getConfig('events') as $eventKey => $event) { if (is_numeric($eventKey)) { $eventKey = $event; $event = null; @@ -75,7 +75,7 @@ public function implementedEvents() if (!is_array($event)) { throw new \InvalidArgumentException('Event should be string or array'); } - $priority = $this->config('priority'); + $priority = $this->getConfig('priority'); if (!array_key_exists('callable', $event) || $event['callable'] === null) { list(, $event['callable']) = pluginSplit($eventKey); } @@ -106,7 +106,7 @@ public function beforeDelete(Event $event, EntityInterface $entity, ArrayObject $event->stopPropagation(); /** @var \Cake\ORM\Table $table */ - $table = $event->subject(); + $table = $event->getSubject(); $table->dispatchEvent('Model.afterDelete', [ 'entity' => $entity, 'options' => $options, @@ -125,10 +125,12 @@ public function beforeDelete(Event $event, EntityInterface $entity, ArrayObject */ public function trash(EntityInterface $entity, array $options = []) { - $primaryKey = (array)$this->_table->primaryKey(); + $primaryKey = (array)$this->_table->getPrimaryKey(); - if (!$entity->has($primaryKey)) { - throw new RuntimeException(); + foreach ($primaryKey as $field) { + if (!$entity->has($field)) { + throw new RuntimeException(); + } } foreach ($this->_table->associations() as $association) { @@ -241,7 +243,7 @@ public function restoreTrash(EntityInterface $entity = null, array $options = [] $data = [$this->getTrashField(false) => null]; if ($entity instanceof EntityInterface) { - if ($entity->dirty()) { + if ($entity->isDirty()) { throw new RuntimeException('Can not restore from a dirty entity.'); } $entity->set($data, ['guard' => false]); @@ -266,14 +268,14 @@ public function cascadingRestoreTrash(EntityInterface $entity = null, array $opt foreach ($this->_table->associations() as $association) { if ($this->_isRecursable($association, $this->_table)) { if ($entity === null) { - $result += $association->target()->cascadingRestoreTrash(null, $options); + $result += $association->getTarget()->cascadingRestoreTrash(null, $options); } else { - $foreignKey = (array)$association->foreignKey(); - $bindingKey = (array)$association->bindingKey(); + $foreignKey = (array)$association->getForeignKey(); + $bindingKey = (array)$association->getBindingKey(); $conditions = array_combine($foreignKey, $entity->extract($bindingKey)); foreach ($association->find('withTrashed')->where($conditions) as $related) { - if (!$association->target()->cascadingRestoreTrash($related, ['_primary' => false] + $options)) { + if (!$association->getTarget()->cascadingRestoreTrash($related, ['_primary' => false] + $options)) { $result = false; } } @@ -306,10 +308,10 @@ protected function _getUnaryExpression() */ public function getTrashField($aliased = true) { - $field = $this->config('field'); + $field = $this->getConfig('field'); if (empty($field)) { - $columns = $this->_table->schema()->columns(); + $columns = $this->_table->getSchema()->columns(); foreach (['deleted', 'trashed'] as $name) { if (in_array($name, $columns, true)) { $field = $name; @@ -325,7 +327,7 @@ public function getTrashField($aliased = true) throw new RuntimeException('TrashBehavior: "field" config needs to be provided.'); } - $this->config('field', $field); + $this->setConfig('field', $field); } if ($aliased) { @@ -344,10 +346,10 @@ public function getTrashField($aliased = true) */ protected function _isRecursable(Association $association, Table $table) { - if ($association->target()->hasBehavior('Trash') + if ($association->getTarget()->hasBehavior('Trash') && $association->isOwningSide($table) - && $association->dependent() - && $association->cascadeCallbacks()) { + && $association->getDependent() + && $association->getCascadeCallbacks()) { return true; } diff --git a/tests/TestCase/Model/Behavior/TrashBehaviorTest.php b/tests/TestCase/Model/Behavior/TrashBehaviorTest.php index 7ad5912..5764ad3 100644 --- a/tests/TestCase/Model/Behavior/TrashBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/TrashBehaviorTest.php @@ -129,13 +129,13 @@ public function testBeforeDelete() */ public function testDeleteOptionsArePassedToCascadingDeletes() { - $association = $this->Articles->association('Comments'); - $association->dependent(true); - $association->cascadeCallbacks(true); + $association = $this->Articles->Comments; + $association->setDependent(true); + $association->setCascadeCallbacks(true); $hasDeleteOptionsBefore = false; $hasDeleteOptionsAfter = false; - $this->Comments->eventManager()->on( + $this->Comments->getEventManager()->on( 'Model.beforeDelete', ['priority' => 1], function (Event $event, EntityInterface $entity, \ArrayObject $options) use (&$hasDeleteOptionsBefore) { @@ -144,7 +144,7 @@ function (Event $event, EntityInterface $entity, \ArrayObject $options) use (&$h } } ); - $this->Comments->eventManager()->on( + $this->Comments->getEventManager()->on( 'Model.afterDelete', function (Event $event, EntityInterface $entity, \ArrayObject $options) use (&$hasDeleteOptionsAfter) { if (isset($options['deleteOptions'])) { @@ -171,14 +171,14 @@ function (Event $event, EntityInterface $entity, \ArrayObject $options) use (&$h */ public function testDeleteOptionsArePassedToSave() { - $association = $this->Articles->association('Comments'); - $association->dependent(true); - $association->cascadeCallbacks(true); + $association = $this->Articles->Comments; + $association->setDependent(true); + $association->setCascadeCallbacks(true); $mainHasDeleteOptions = false; $dependentHasDeleteOptions = false; $dependentIsNotPrimary = false; - $this->Articles->eventManager()->on( + $this->Articles->getEventManager()->on( 'Model.beforeSave', function (Event $event, EntityInterface $entity, \ArrayObject $options) use (&$mainHasDeleteOptions) { if (isset($options['deleteOptions'])) { @@ -186,7 +186,7 @@ function (Event $event, EntityInterface $entity, \ArrayObject $options) use (&$m } } ); - $this->Comments->eventManager()->on( + $this->Comments->getEventManager()->on( 'Model.beforeSave', function ( Event $event, @@ -251,7 +251,7 @@ public function testTrash() public function testTrashNonAccessibleProperty() { $article = $this->Articles->get(1); - $article->accessible('trashed', false); + $article->setAccess('trashed', false); $result = $this->Articles->trash($article); $this->assertTrue($result); @@ -388,9 +388,9 @@ public function testInteroperabilityWithCounterCacheAndTrashMethod() */ public function testCascadingTrash() { - $association = $this->Articles->association('Comments'); - $association->dependent(true); - $association->cascadeCallbacks(true); + $association = $this->Articles->Comments; + $association->setDependent(true); + $association->setCascadeCallbacks(true); $article = $this->Articles->get(1); $this->Articles->trash($article); @@ -411,12 +411,12 @@ public function testCascadingTrash() public function testCascadingUntrashOptionsArePassedToSave() { - $association = $this->Articles->association('Comments'); - $association->dependent(true); - $association->cascadeCallbacks(true); + $association = $this->Articles->Comments; + $association->setDependent(true); + $association->setCascadeCallbacks(true); - $this->Articles->Comments->target()->trashAll([]); - $this->assertEquals(0, $this->Articles->Comments->target()->find()->count()); + $this->Articles->Comments->getTarget()->trashAll([]); + $this->assertEquals(0, $this->Articles->Comments->getTarget()->find()->count()); $this->Articles->trashAll([]); $this->assertEquals(0, $this->Articles->find()->count()); @@ -429,7 +429,7 @@ public function testCascadingUntrashOptionsArePassedToSave() $mainHasRestoreOptions = false; $dependentHasRestoreOptions = false; $dependentIsNotPrimary = false; - $this->Articles->eventManager()->on( + $this->Articles->getEventManager()->on( 'Model.beforeSave', function (Event $event, EntityInterface $entity, \ArrayObject $options) use (&$mainHasRestoreOptions) { if (isset($options['restoreOptions'])) { @@ -437,7 +437,7 @@ function (Event $event, EntityInterface $entity, \ArrayObject $options) use (&$m } } ); - $this->Comments->eventManager()->on( + $this->Comments->getEventManager()->on( 'Model.beforeSave', function ( Event $event, @@ -473,19 +473,19 @@ function ( */ public function testCascadingUntrashEntity() { - $association = $this->Articles->association('Comments'); - $association->dependent(true); - $association->cascadeCallbacks(true); + $association = $this->Articles->Comments; + $association->setDependent(true); + $association->setCascadeCallbacks(true); - $association = $this->Articles->association('CompositeArticlesUsers'); - $association->dependent(true); - $association->cascadeCallbacks(true); + $association = $this->Articles->CompositeArticlesUsers; + $association->setDependent(true); + $association->setCascadeCallbacks(true); - $this->Articles->Comments->target()->trashAll([]); - $this->assertEquals(0, $this->Articles->Comments->target()->find()->count()); + $this->Articles->Comments->getTarget()->trashAll([]); + $this->assertEquals(0, $this->Articles->Comments->getTarget()->find()->count()); - $this->Articles->CompositeArticlesUsers->target()->trashAll([]); - $this->assertEquals(0, $this->Articles->CompositeArticlesUsers->target()->find()->count()); + $this->Articles->CompositeArticlesUsers->getTarget()->trashAll([]); + $this->assertEquals(0, $this->Articles->CompositeArticlesUsers->getTarget()->find()->count()); $this->Articles->trashAll([]); $this->assertEquals(0, $this->Articles->find()->count()); @@ -512,12 +512,12 @@ public function testCascadingUntrashEntity() $this->assertNotEmpty($article->composite_articles_users[0]->trashed); $this->assertInstanceOf('Cake\I18n\Time', $article->composite_articles_users[0]->trashed); - $unrelatedComment = $this->Articles->Comments->target()->findById(3)->find('withTrashed')->first(); + $unrelatedComment = $this->Articles->Comments->getTarget()->findById(3)->find('withTrashed')->first(); $this->assertNotEquals($article->id, $unrelatedComment->article_id); $this->assertNotEmpty($unrelatedComment->trashed); $this->assertInstanceOf('Cake\I18n\Time', $unrelatedComment->trashed); - $unrelatedArticleUser = $this->Articles->CompositeArticlesUsers->target()->findByArticleId(3)->find('withTrashed')->first(); + $unrelatedArticleUser = $this->Articles->CompositeArticlesUsers->getTarget()->findByArticleId(3)->find('withTrashed')->first(); $this->assertNotEquals($article->id, $unrelatedArticleUser->article_id); $this->assertNotEmpty($unrelatedArticleUser->trashed); $this->assertInstanceOf('Cake\I18n\Time', $unrelatedArticleUser->trashed); @@ -534,12 +534,12 @@ public function testCascadingUntrashEntity() $this->assertEmpty($article->comments[0]->trashed); $this->assertEmpty($article->composite_articles_users[0]->trashed); - $unrelatedComment = $this->Articles->Comments->target()->findById(3)->find('withTrashed')->first(); + $unrelatedComment = $this->Articles->Comments->getTarget()->findById(3)->find('withTrashed')->first(); $this->assertNotEquals($article->id, $unrelatedComment->article_id); $this->assertNotEmpty($unrelatedComment->trashed); $this->assertInstanceOf('Cake\I18n\Time', $unrelatedComment->trashed); - $unrelatedArticleUser = $this->Articles->CompositeArticlesUsers->target()->findByArticleId(3)->find('withTrashed')->first(); + $unrelatedArticleUser = $this->Articles->CompositeArticlesUsers->getTarget()->findByArticleId(3)->find('withTrashed')->first(); $this->assertNotEquals($article->id, $unrelatedArticleUser->article_id); $this->assertNotEmpty($unrelatedArticleUser->trashed); $this->assertInstanceOf('Cake\I18n\Time', $unrelatedArticleUser->trashed); @@ -552,19 +552,19 @@ public function testCascadingUntrashEntity() */ public function testCascadingUntrashAll() { - $association = $this->Articles->association('Comments'); - $association->dependent(true); - $association->cascadeCallbacks(true); + $association = $this->Articles->Comments; + $association->setDependent(true); + $association->setCascadeCallbacks(true); - $association = $this->Articles->association('CompositeArticlesUsers'); - $association->dependent(true); - $association->cascadeCallbacks(true); + $association = $this->Articles->CompositeArticlesUsers; + $association->setDependent(true); + $association->setCascadeCallbacks(true); - $this->Articles->Comments->target()->trashAll([]); - $this->assertEquals(0, $this->Articles->Comments->target()->find()->count()); + $this->Articles->Comments->getTarget()->trashAll([]); + $this->assertEquals(0, $this->Articles->Comments->getTarget()->find()->count()); - $this->Articles->CompositeArticlesUsers->target()->trashAll([]); - $this->assertEquals(0, $this->Articles->CompositeArticlesUsers->target()->find()->count()); + $this->Articles->CompositeArticlesUsers->getTarget()->trashAll([]); + $this->assertEquals(0, $this->Articles->CompositeArticlesUsers->getTarget()->find()->count()); $this->Articles->trashAll([]); $this->assertEquals(0, $this->Articles->find()->count()); @@ -603,8 +603,8 @@ public function testCascadingUntrashAll() $this->assertEmpty($article->comments[0]->trashed); $this->assertEmpty($article->composite_articles_users[0]->trashed); - $this->assertEquals(3, $this->Articles->Comments->target()->find()->count()); - $this->assertEquals(2, $this->Articles->CompositeArticlesUsers->target()->find()->count()); + $this->assertEquals(3, $this->Articles->Comments->getTarget()->find()->count()); + $this->assertEquals(2, $this->Articles->CompositeArticlesUsers->getTarget()->find()->count()); $this->assertEquals(3, $this->Articles->find()->count()); } @@ -615,15 +615,15 @@ public function testCascadingUntrashAll() */ public function testCascadingUntrashFailure() { - $association = $this->Articles->association('Comments'); - $association->dependent(true); - $association->cascadeCallbacks(true); - $association->eventManager()->on('Model.beforeSave', function () { + $association = $this->Articles->Comments; + $association->setDependent(true); + $association->setCascadeCallbacks(true); + $association->getEventManager()->on('Model.beforeSave', function () { return false; }); - $association->target()->trashAll([]); - $this->assertEquals(0, $association->target()->find()->count()); + $association->getTarget()->trashAll([]); + $this->assertEquals(0, $association->getTarget()->find()->count()); $this->Articles->trashAll([]); $this->assertEquals(0, $this->Articles->find()->count()); @@ -643,10 +643,10 @@ public function testCascadingUntrashFailure() */ public function testTrashDependentViaReplaceSaveStrategy() { - $association = $this->Articles->association('Comments'); - $association->dependent(true); - $association->cascadeCallbacks(true); - $association->saveStrategy(HasMany::SAVE_REPLACE); + $association = $this->Articles->Comments; + $association->setDependent(true); + $association->setCascadeCallbacks(true); + $association->setSaveStrategy(HasMany::SAVE_REPLACE); $article = $this->Articles->get(1, [ 'contain' => ['Comments'] @@ -656,7 +656,7 @@ public function testTrashDependentViaReplaceSaveStrategy() $this->assertEmpty($article->comments[0]->trashed); $article->set('comments', []); - $article->dirty('comments', true); + $article->setDirty('comments', true); $this->assertInstanceOf('Cake\Datasource\EntityInterface', $this->Articles->save($article)); @@ -702,6 +702,21 @@ public function testGetTrashFieldUsesConfiguredValue() $this->assertEquals('Users.trashed', $trash->getTrashField()); } + /** + * Test that getTrashField() uses a default value if no field is configured and that it sets the name of the field + * in the config array. + * + * @return void + */ + public function testGetTrashFieldFallbackToDefault() + { + $trash = new TrashBehavior($this->Articles, ['field' => '']); + + $this->assertEmpty($trash->getConfig('field')); + $this->assertEquals('Articles.trashed', $trash->getTrashField()); + $this->assertEquals('trashed', $trash->getConfig('field')); + } + /** * Test that getTrashField() defaults to deleted or trashed * when found in schema and not specified