diff --git a/README.md b/README.md index c1a4a8b..dfa786d 100644 --- a/README.md +++ b/README.md @@ -136,10 +136,24 @@ $user->undoPoint(new PostCreated($post)); $post->delete(); ``` -You can also pass second argument as $user in helper function `givePoint(new PostCreated($post, $user))`, default is auth()->user(). +You can also pass second argument as $user in helper function `givePoint(new PostCreated($post) $user)`, default is auth()->user(). **Pro Tip 👌** You could also hook into the Eloquent model event and give point on `created` event. Similarly, `deleted` event can be used to undo the point. +### Give or update points to User + +There may be a situation where the points given for a reputation record changes over time. For example, a comment to a post that can be up voted. Those votes are given to the commenter as they come in. + +For this situation you can use the `giveOrUpdatePoint` helper which will give the points or update them automatically to the user's reputation total + +``` php +// create initial reputation record with 5 points +giveOrUpdatePoint(new PostCommentVotes($postComment, 5), $user); + +// update reputation record to 20 points for the same $postComment db record +giveOrUpdatePoint(new PostCommentVotes($postComment, 20), $user); +``` + ### Get total reputation To get the total user reputation you have `$user->getPoints($formatted = false)` method available. Optioally you can pass `$formatted = true` to get reputation as 1K+, 2K+ etc. diff --git a/src/GamifyServiceProvider.php b/src/GamifyServiceProvider.php index af2335a..ac88707 100644 --- a/src/GamifyServiceProvider.php +++ b/src/GamifyServiceProvider.php @@ -10,6 +10,11 @@ use QCod\Gamify\Console\MakePointCommand; use QCod\Gamify\Events\ReputationChanged; +use \RecursiveDirectoryIterator; +use \RecursiveIteratorIterator; +use \RecursiveRegexIterator; +use \RegexIterator; + class GamifyServiceProvider extends ServiceProvider { /** @@ -77,10 +82,31 @@ protected function getBadges() $badges = []; - foreach (glob(app_path('/Gamify/Badges/') . '*.php') as $file) { - if (is_file($file)) { - $badges[] = app($badgeRootNamespace . '\\' . pathinfo($file, PATHINFO_FILENAME)); - } + // Get the first folder for the app. For the vast majority of all projects this is "App" + $rootFolder = substr($badgeRootNamespace, 0, strpos($badgeRootNamespace, '\\')); + + // Create recursive searching classes + $directory = new RecursiveDirectoryIterator(app_path('Gamify/Badges/')); + $iterator = new RecursiveIteratorIterator($directory); + $files = new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH); + + // loop through each file found + foreach ($files as $file) { + + // grab the directory for the file + $fileDirectory = pathinfo($file[0], PATHINFO_DIRNAME); + + //remove full server path and prepend the rootfolder + $fileDirectory = $rootFolder.str_ireplace(app_path(), '', $fileDirectory); + + // convert the forward slashes to backslashes + $fileDirectory = str_ireplace('/', '\\', $fileDirectory); + + // get the file name + $fileName = pathinfo($file[0], PATHINFO_FILENAME); + + //append namespace file path to the badges array to return + $badges[] = $fileDirectory."\\".$fileName; } return collect($badges); diff --git a/src/HasReputations.php b/src/HasReputations.php index 8b46717..846785c 100644 --- a/src/HasReputations.php +++ b/src/HasReputations.php @@ -23,6 +23,26 @@ public function givePoint(PointType $pointType) } } + /** + * Give or update reputation points to payee + * + * @param PointType $pointType + * @return bool + */ + public function giveOrUpdatePoint(PointType $pointType) + { + if (!$pointType->qualifier()) { + return false; + } + + if ($pointType->reputationExists()) { + $originalPoints = $pointType->syncPointsChange(); + return $pointType->payee()->syncPointsChange($originalPoints, $pointType->getPoints()); + }else if ($this->storeReputation($pointType)) { + return $pointType->payee()->addPoint($pointType->getPoints()); + } + } + /** * Undo last given point for a subject model * @@ -82,6 +102,22 @@ public function addPoint($point = 1) return $this; } + /** + * Sync points changed for a reputation record to a payee + * + * @param int $point + * @return HasReputations|\Illuminate\Database\Eloquent\Model + */ + public function syncPointsChange($originalPoints, $newPoints = 1) + { + $this->decrement($this->getReputationField(), $originalPoints); + $this->increment($this->getReputationField(), $newPoints); + + ReputationChanged::dispatch($this, $newPoints, true); + + return $this; + } + /** * Reduce a user point * diff --git a/src/PointType.php b/src/PointType.php index 2ef2c49..75daaba 100644 --- a/src/PointType.php +++ b/src/PointType.php @@ -134,6 +134,20 @@ public function storeReputation($meta = []) ]); } + /** + * Sync updates to points to an existing reputation record + * @return int $originalPoints + */ + public function syncPointsChange() + { + $dbPointType = $this->firstReputation(); + $originalPoints = $dbPointType->point; + $dbPointType->update([ + 'point' => $this->getPoints() + ]); + return $originalPoints; + } + /** * Get reputation query for this point * diff --git a/src/helpers.php b/src/helpers.php index bcf390d..b482c02 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -22,6 +22,26 @@ function givePoint(PointType $pointType, $payee = null) } } +if (!function_exists('giveOrUpdatePoint')) { + + /** + * Give point to user + * + * @param PointType $pointType + * @param null $payee + */ + function giveOrUpdatePoint(PointType $pointType, $payee = null) + { + $payee = $payee ?? auth()->user(); + + if (!$payee) { + return; + } + + $payee->giveOrUpdatePoint($pointType); + } +} + if (!function_exists('undoPoint')) { /** diff --git a/tests/PointTest.php b/tests/PointTest.php index 13443e3..4c4c699 100644 --- a/tests/PointTest.php +++ b/tests/PointTest.php @@ -69,6 +69,41 @@ public function it_gives_point_to_a_user() ]); } + /** + * it gives point to a user, then updates it validating that that change + * + * @test + */ + public function it_gives_and_updates_points_to_a_user() + { + $user = $this->createUser(); + $post = $this->createPost(['user_id' => $user->id]); + + $user->giveOrUpdatePoint(new FakeUpdateablePostPoint($post)); + + $this->assertEquals(1, $user->fresh()->getPoints()); + $this->assertCount(1, $user->reputations); + $this->assertDatabaseHas('reputations', [ + 'payee_id' => $user->id, + 'subject_type' => $post->getMorphClass(), + 'subject_id' => $post->id, + 'point' => 1, + 'name' => 'FakeUpdateablePostPoint' + ]); + + $user->giveOrUpdatePoint(new FakeUpdateablePostPoint($post, 5)); + + $this->assertEquals(5, $user->fresh()->getPoints()); + $this->assertCount(1, $user->reputations); + $this->assertDatabaseHas('reputations', [ + 'payee_id' => $user->id, + 'subject_type' => $post->getMorphClass(), + 'subject_id' => $post->id, + 'point' => 5, + 'name' => 'FakeUpdateablePostPoint' + ]); + } + /** * it can access a reputation payee and subject * @@ -269,6 +304,24 @@ public function payee() } } +class FakeUpdateablePostPoint extends PointType +{ + protected $points = 1; + + public $allowDuplicates = false; + + public function __construct($subject, $points = 1) + { + $this->subject = $subject; + $this->points = $points; + } + + public function payee() + { + return $this->getSubject()->user; + } +} + class FakeWelcomeUserWithNamePoint extends PointType { protected $name = 'FakeName';