Skip to content

Commit 9adc19d

Browse files
authored
Merge pull request #1 from ajimoti/MagicCalls
Include support for magic calls.
2 parents c85fcd0 + 0a46b5d commit 9adc19d

File tree

6 files changed

+146
-2
lines changed

6 files changed

+146
-2
lines changed

src/HasRoles.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
use Ajimoti\RolesAndPermissions\Models\ModelRole;
1010
use Ajimoti\RolesAndPermissions\Repositories\BelongsToManyRepository;
1111
use Ajimoti\RolesAndPermissions\Repositories\ModelRepository;
12+
use Ajimoti\RolesAndPermissions\Traits\HasDirectPermissions;
13+
use Ajimoti\RolesAndPermissions\Traits\SupportsMagicCalls;
1214
use Illuminate\Database\Eloquent\Model;
1315

1416
trait HasRoles
1517
{
1618
use HasDirectPermissions;
19+
use SupportsMagicCalls;
1720

1821
/**
1922
* Change the repository used to the pivot table repository
@@ -205,4 +208,16 @@ public function repository(): ModelRepository
205208
{
206209
return new ModelRepository($this);
207210
}
211+
212+
public function __call($method, $parameters)
213+
{
214+
if ($this->isPossibleMagicCall($method)) {
215+
return $this->performMagic(
216+
$method,
217+
$this->repository()->getRoleEnumClass()
218+
);
219+
}
220+
221+
return parent::__call($method, $parameters);
222+
}
208223
}

src/Repositories/BelongsToManyRepository.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Ajimoti\RolesAndPermissions\Facades\Check;
1111
use Ajimoti\RolesAndPermissions\Helpers\BasePermission;
1212
use Ajimoti\RolesAndPermissions\Helpers\Pivot;
13+
use Ajimoti\RolesAndPermissions\Traits\SupportsMagicCalls;
1314
use BadMethodCallException;
1415
use Illuminate\Database\Eloquent\Model;
1516
use Illuminate\Support\Facades\DB;
@@ -19,6 +20,7 @@
1920
class BelongsToManyRepository implements RolesContract, PivotContract
2021
{
2122
use Authorizable;
23+
use SupportsMagicCalls;
2224

2325
/**
2426
* Methods used to filter results when defining a 'belongsToMany' relationship
@@ -205,6 +207,7 @@ public function removeRoles(): bool
205207
return $query->detach();
206208
}
207209

210+
// If the role enum class isn't set to delete pivot records, we'll just set the role column to null.
208211
return $query->updateExistingPivot($this->relatedModel->id, [$this->roleColumnName => null]);
209212
}
210213

@@ -248,6 +251,13 @@ public function __call($method, $parameters)
248251
return $this;
249252
}
250253

254+
if ($this->isPossibleMagicCall($method)) {
255+
return $this->performMagic(
256+
$method,
257+
$this->pivot->getRoleEnumClass()
258+
);
259+
}
260+
251261
throw new BadMethodCallException(sprintf(
252262
'Call to undefined method %s::%s()',
253263
static::class,

src/Repositories/ModelRepository.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ public function revoke(...$permissions): bool
265265
*
266266
* @return string
267267
*/
268-
private function getRoleEnumClass(): string
268+
public function getRoleEnumClass(): string
269269
{
270270
$tableName = $this->model->getTable();
271271

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Ajimoti\RolesAndPermissions;
3+
namespace Ajimoti\RolesAndPermissions\Traits;
44

55
use Ajimoti\RolesAndPermissions\Collections\PermissionCollection;
66
use Ajimoti\RolesAndPermissions\Models\ModelPermission;

src/Traits/SupportsMagicCalls.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace Ajimoti\RolesAndPermissions\Traits;
4+
5+
use Illuminate\Support\Str;
6+
7+
trait SupportsMagicCalls
8+
{
9+
/**
10+
* Method to call on the model to check if the model has the role.
11+
*
12+
* @return bool
13+
*/
14+
abstract public function hasRole(): bool;
15+
16+
/**
17+
* Method to call on the model to check if the model holds the permissions.
18+
*
19+
* @return bool
20+
*/
21+
abstract public function holds(): bool;
22+
23+
/**
24+
* Checks if the magic method called is to validate a role or a permission
25+
*
26+
* @param string $method
27+
* @return bool
28+
*/
29+
private function isPossibleMagicCall($method): bool
30+
{
31+
// When the user uses the magic method is[Role]()
32+
// to check if the model has the role.
33+
// OR when the user uses the magic method can[Permission]()
34+
// to check if the model has the permission.
35+
return (Str::startsWith($method, 'is') && Str::length($method) > 2) ||
36+
(Str::startsWith($method, 'can') && Str::length($method) > 3);
37+
}
38+
39+
public function performMagic($method, $enumClass)
40+
{
41+
if (Str::startsWith($method, 'is') && Str::length($method) > 2) {
42+
// When the user uses the magic method is[Role]()
43+
// to check if the model has the role.
44+
$role = substr($method, 2);
45+
46+
return $this->hasRole(constant("{$enumClass}::{$role}"));
47+
} elseif (Str::startsWith($method, 'can') && Str::length($method) > 3) {
48+
// When the user uses the magic method can[Permission]()
49+
// to check if the model has the permission.
50+
$permission = substr($method, 3);
51+
$permissionClass = $enumClass::$permissionClass;
52+
53+
return $this->holds(constant("{$permissionClass}::{$permission}"));
54+
}
55+
}
56+
}

tests/Feature/MagicMethodsTest.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
use Ajimoti\RolesAndPermissions\Tests\Enums\MerchantRole;
4+
use Ajimoti\RolesAndPermissions\Tests\Models\Merchant;
5+
use Ajimoti\RolesAndPermissions\Tests\Models\User;
6+
7+
beforeEach(function () {
8+
config()->set('roles-and-permissions.roles_enum.merchant_user', MerchantRole::class);
9+
10+
$this->model = Merchant::factory()->create();
11+
$this->user = User::factory()->create();
12+
$this->role = MerchantRole::getRandomValue();
13+
$this->roleInstance = MerchantRole::fromValue($this->role);
14+
15+
$this->user->of($this->model)->assign($this->role);
16+
$this->model->assign($this->role);
17+
});
18+
19+
it('can check that a model has a role using magic method', function () {
20+
expect($this->model->hasRole($this->role))->toBeTrue();
21+
22+
expect($this->model->{"is".$this->roleInstance->key}())->toBeTrue();
23+
})->group('MagicMethods');
24+
25+
it('can check that a model has a permission using magic method', function () {
26+
expect($this->model->hasRole($this->role))->toBeTrue();
27+
28+
foreach ($this->roleInstance->permissions as $permission) {
29+
expect($this->model->{"can".$permission->key}())->toBeTrue();
30+
}
31+
})->group('MagicMethods');
32+
33+
it('can check that a many-to-many relationship has a role using magic method', function () {
34+
expect($this->user->of($this->model)->hasRole($this->role))->toBeTrue();
35+
36+
expect($this->user->of($this->model)->{"is".$this->roleInstance->key}())->toBeTrue();
37+
})->group('MagicMethods');
38+
39+
it('can check that a many-to-many relationship has a permission using magic method', function () {
40+
expect($this->user->of($this->model)->hasRole($this->role))->toBeTrue();
41+
42+
foreach ($this->roleInstance->permissions as $permission) {
43+
expect($this->user->of($this->model)->{"can".$permission->key}())->toBeTrue();
44+
}
45+
})->group('MagicMethods');
46+
47+
it('magic method functionality does not interfere with laravel `is()` method', function () {
48+
expect($this->user->is(User::factory()->create()))->toBeFalse();
49+
50+
expect($this->user->is($this->user))->toBeTrue();
51+
})->group('MagicMethods');
52+
53+
it('magic method functionality does not interfere with laravel `can()` method', function () {
54+
expect($this->model->can("a_random_permission_ha_ha_ha"))->toBeFalse();
55+
56+
// Cases where MerchantRole::Customer is the assigned role,
57+
// the permissions list will be empty
58+
if ($this->roleInstance->permissions->isNotEmpty()) {
59+
$permission = $this->roleInstance->permissions->random();
60+
61+
expect($this->model->can($permission->value))->toBeTrue();
62+
}
63+
})->group('MagicMethods');

0 commit comments

Comments
 (0)