generated from spatie/package-skeleton-laravel
-
-
Notifications
You must be signed in to change notification settings - Fork 210
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Filling properties from current user
- Loading branch information
Showing
9 changed files
with
371 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
|
||
namespace Spatie\LaravelData\Attributes\FromData; | ||
|
||
use Attribute; | ||
use Illuminate\Http\Request; | ||
use Spatie\LaravelData\Support\Creation\CreationContext; | ||
use Spatie\LaravelData\Support\DataProperty; | ||
|
||
#[Attribute(Attribute::TARGET_PROPERTY)] | ||
class FromCurrentUser implements FromDataAttribute | ||
{ | ||
/** | ||
* @param class-string|null $userClass | ||
*/ | ||
public function __construct( | ||
public ?string $guard = null, | ||
public bool $replaceWhenPresentInBody = true, | ||
public ?string $userClass = null | ||
) { | ||
} | ||
|
||
public function resolve( | ||
DataProperty $dataProperty, | ||
mixed $payload, | ||
array $properties, | ||
CreationContext $creationContext | ||
): mixed { | ||
if (! $payload instanceof Request) { | ||
return null; | ||
} | ||
|
||
$name = $dataProperty->getInputName(); | ||
if (! $this->replaceWhenPresentInBody && array_key_exists($name, $properties)) { | ||
return null; | ||
} | ||
|
||
$user = $payload->user($this->guard); | ||
|
||
if ( | ||
$user === null | ||
|| ($this->userClass && ! ($user instanceof $this->userClass))) { | ||
return null; | ||
} | ||
|
||
return $user; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
|
||
namespace Spatie\LaravelData\Attributes\FromData; | ||
|
||
use Attribute; | ||
use Illuminate\Http\Request; | ||
use Spatie\LaravelData\Support\Creation\CreationContext; | ||
use Spatie\LaravelData\Support\DataProperty; | ||
|
||
#[Attribute(Attribute::TARGET_PROPERTY)] | ||
class FromCurrentUserProperty implements FromDataAttribute | ||
{ | ||
public function __construct( | ||
public ?string $guard = null, | ||
public ?string $property = null, | ||
public bool $replaceWhenPresentInBody = true, | ||
public ?string $userClass = null | ||
) { | ||
} | ||
|
||
public function resolve( | ||
DataProperty $dataProperty, | ||
mixed $payload, | ||
array $properties, | ||
CreationContext $creationContext | ||
): mixed { | ||
if (! $payload instanceof Request) { | ||
return null; | ||
} | ||
|
||
$fromCurrentUser = new FromCurrentUser($this->guard, $this->replaceWhenPresentInBody, $this->userClass); | ||
$user = $fromCurrentUser->resolve($dataProperty, $payload, $properties, $creationContext); | ||
if ($user === null) { | ||
return null; | ||
} | ||
|
||
return data_get($user, $this->property ?? $dataProperty->name); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php | ||
|
||
namespace Spatie\LaravelData\Attributes\FromData; | ||
|
||
use Spatie\LaravelData\Support\Creation\CreationContext; | ||
use Spatie\LaravelData\Support\DataProperty; | ||
|
||
interface FromDataAttribute | ||
{ | ||
public function resolve( | ||
DataProperty $dataProperty, | ||
mixed $payload, | ||
array $properties, | ||
CreationContext $creationContext | ||
): mixed; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
namespace Spatie\LaravelData\DataPipes; | ||
|
||
use Spatie\LaravelData\Attributes\FromData\FromDataAttribute; | ||
use Spatie\LaravelData\Support\Creation\CreationContext; | ||
use Spatie\LaravelData\Support\DataClass; | ||
|
||
class FillDataPipe implements DataPipe | ||
{ | ||
public function handle(mixed $payload, DataClass $class, array $properties, CreationContext $creationContext): array | ||
{ | ||
|
||
foreach ($class->properties as $dataProperty) { | ||
/** @var null|FromDataAttribute $attribute */ | ||
$attribute = $dataProperty->attributes->first( | ||
fn (object $attribute) => $attribute instanceof FromDataAttribute | ||
); | ||
if ($attribute === null) { | ||
continue; | ||
} | ||
|
||
$value = $attribute->resolve($dataProperty, $payload, $properties, $creationContext); | ||
if ($value === null) { | ||
continue; | ||
} | ||
|
||
$dataProperty->setValueForProperties($properties, $value); | ||
} | ||
|
||
return $properties; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
<?php | ||
|
||
namespace Spatie\LaravelData\Tests; | ||
|
||
use Illuminate\Foundation\Auth\User; | ||
use Illuminate\Http\Request; | ||
use Spatie\LaravelData\Attributes\FromData\FromCurrentUser; | ||
use Spatie\LaravelData\Attributes\FromData\FromCurrentUserProperty; | ||
use Spatie\LaravelData\Data; | ||
use stdClass; | ||
|
||
it('can fill data properties with current user', function () { | ||
$dataClass = new class () extends Data { | ||
#[FromCurrentUser] | ||
public User $user; | ||
}; | ||
|
||
$user = new User(); | ||
$requestMock = mock(Request::class); | ||
$requestMock->expects('user')->once()->andReturns($user); | ||
$requestMock->expects('toArray')->once()->andReturns([]); | ||
|
||
$data = $dataClass::from($requestMock); | ||
|
||
expect($data->user)->toBe($user); | ||
}); | ||
|
||
it('can fill data properties from current user properties', function () { | ||
$dataClass = new class () extends Data { | ||
#[FromCurrentUserProperty] | ||
public int $id; | ||
#[FromCurrentUserProperty] | ||
public string $name; | ||
#[FromCurrentUserProperty] | ||
public string $email; | ||
}; | ||
|
||
$user = new User(); | ||
$user->id = 123; | ||
$user->name = 'John Doe'; | ||
$user->email = '[email protected]'; | ||
|
||
$requestMock = mock(Request::class); | ||
$requestMock->expects('user')->times(3)->andReturns($user); | ||
$requestMock->expects('toArray')->once()->andReturns([]); | ||
|
||
$data = $dataClass::from($requestMock); | ||
|
||
expect($data->id)->toEqual($user->id); | ||
expect($data->name)->toEqual($user->name); | ||
expect($data->email)->toEqual($user->email); | ||
}); | ||
|
||
it('can fill data properties from specified current user property', function () { | ||
$dataClass = new class () extends Data { | ||
#[FromCurrentUserProperty(property: 'id')] | ||
public int $userId; | ||
#[FromCurrentUserProperty(property: 'name')] | ||
public string $userName; | ||
#[FromCurrentUserProperty(property: 'email')] | ||
public string $userEmail; | ||
}; | ||
|
||
$user = new User(); | ||
$user->id = 123; | ||
$user->name = 'Jane Doe'; | ||
$user->email = '[email protected]'; | ||
|
||
$requestMock = mock(Request::class); | ||
$requestMock->expects('user')->times(3)->andReturns($user); | ||
$requestMock->expects('toArray')->once()->andReturns([]); | ||
|
||
$data = $dataClass::from($requestMock); | ||
|
||
expect($data->userId)->toEqual($user->id); | ||
expect($data->userName)->toEqual($user->name); | ||
expect($data->userEmail)->toEqual($user->email); | ||
}); | ||
|
||
it('can fill data properties with current user using specified guard', function () { | ||
$dataClass = new class () extends Data { | ||
#[FromCurrentUser('api')] | ||
public User $user; | ||
}; | ||
|
||
$user = new User(); | ||
$requestMock = mock(Request::class); | ||
$requestMock->expects('user')->with('api')->once()->andReturns($user); | ||
$requestMock->expects('toArray')->once()->andReturns([]); | ||
|
||
$data = $dataClass::from($requestMock); | ||
|
||
expect($data->user)->toBe($user); | ||
}); | ||
|
||
it('replaces properties when current user exists', function () { | ||
$dataClass = new class () extends Data { | ||
#[FromCurrentUser] | ||
public User $user; | ||
}; | ||
|
||
$user = new User(); | ||
$requestMock = mock(Request::class); | ||
$requestMock->expects('user')->once()->andReturns($user); | ||
$requestMock->expects('toArray')->once()->andReturns(['user' => new User()]); | ||
|
||
$data = $dataClass::from($requestMock); | ||
|
||
expect($data->user)->toBe($user); | ||
}); | ||
|
||
it('disables replacing properties when current user exists', function () { | ||
$dataClass = new class () extends Data { | ||
#[FromCurrentUser(replaceWhenPresentInBody: false)] | ||
public User $user; | ||
}; | ||
|
||
$requestMock = mock(Request::class); | ||
$requestMock->expects('user')->never(); | ||
$user = new User(); | ||
$requestMock->expects('toArray')->once()->andReturns(['user' => $user]); | ||
|
||
$data = $dataClass::from($requestMock); | ||
|
||
expect($data->user)->toBe($user); | ||
}); | ||
|
||
it('fills data properties when replacement is disabled and current user exists with non-existent input property', function () { | ||
$dataClass = new class () extends Data { | ||
#[FromCurrentUser(replaceWhenPresentInBody: false)] | ||
public ?User $user = null; | ||
}; | ||
|
||
$user = new User(); | ||
$requestMock = mock(Request::class); | ||
$requestMock->expects('user')->once()->andReturns($user); | ||
$requestMock->expects('toArray')->once()->andReturns([]); | ||
|
||
$data = $dataClass::from($requestMock); | ||
|
||
expect($data->user)->toBe($user); | ||
}); | ||
|
||
it('can fill data properties with current user using specified userClass', function () { | ||
$dataClass = new class () extends Data { | ||
#[FromCurrentUser(userClass: User::class)] | ||
public User $user; | ||
}; | ||
|
||
$user = new User(); | ||
$requestMock = mock(Request::class); | ||
$requestMock->expects('user')->once()->andReturns($user); | ||
$requestMock->expects('toArray')->once()->andReturns([]); | ||
$data = $dataClass::factory()->withoutValidation()->from($requestMock); | ||
expect($data->user)->toBeInstanceOf(User::class); | ||
}); | ||
|
||
it('skips filling data properties when specified userClass does not match', function () { | ||
$dataClass = new class () extends Data { | ||
#[FromCurrentUser(userClass: User::class)] | ||
public ?User $user = null; | ||
}; | ||
|
||
$user = new stdClass(); | ||
$requestMock = mock(Request::class); | ||
$requestMock->expects('user')->once()->andReturns($user); | ||
$requestMock->expects('toArray')->once()->andReturns([]); | ||
|
||
$data = $dataClass::from($requestMock); | ||
|
||
expect($data->user)->toBeNull(); | ||
}); |
Oops, something went wrong.