Last active
July 25, 2020 08:09
-
-
Save webdevilopers/93b82fedd25a9d43d2af64de2fa0c57f to your computer and use it in GitHub Desktop.
How not allow extra fields in Command DTO using Symfony Messenger
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
<?php | |
use Webmozart\Assert\Assert; | |
final class Address | |
{ | |
/** @var string|null $street */ | |
private $street; | |
/** @var Postcode|null $postcode */ | |
private $postcode; | |
/** @var string|null $city */ | |
private $city; | |
/** @var CountryCode $countryCode */ | |
private $countryCode; | |
private function __construct(?string $aStreet, ?Postcode $aPostcode, ?string $aCity, CountryCode $aCountryCode) | |
{ | |
Assert::nullOrNotEmpty($aStreet); | |
Assert::nullOrNotEmpty($aCity); | |
$this->street = $aStreet; | |
$this->postcode = $aPostcode; | |
$this->city = $aCity; | |
$this->countryCode = $aCountryCode; | |
} | |
public static function fromArray(array $array): Address | |
{ | |
Assert::keyExists($array, 'street'); | |
Assert::nullOrString($array['street']); | |
Assert::keyExists($array, 'postcode'); | |
Assert::nullOrString($array['postcode']); | |
Assert::keyExists($array, 'city'); | |
Assert::nullOrString($array['city']); | |
Assert::keyExists($array, 'countryCode'); | |
Assert::nullOrString($array['countryCode']); | |
return new self( | |
$array['street'], | |
null !== $array['postcode'] ? Postcode::fromString($array['postcode']) : null, | |
$array['city'], | |
null !== $array['countryCode'] ? CountryCode::fromString($array['countryCode']) : null | |
); | |
} | |
public function toArray(): array | |
{ | |
return [ | |
'street' => $this->street, | |
'postcode' => null !== $this->postcode ? $this->postcode->toString() : null, | |
'city' => $this->city, | |
'countryCode' => null !== $this->countryCode ? $this->countryCode->toString() : null | |
]; | |
} | |
public function street(): ?string | |
{ | |
return $this->street; | |
} | |
public function postcode(): ?Postcode | |
{ | |
return $this->postcode; | |
} | |
public function city(): ?string | |
{ | |
return $this->city; | |
} | |
public function countryCode(): CountryCode | |
{ | |
return $this->countryCode; | |
} | |
} |
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
<?php | |
final class AgencyController | |
{ | |
/** @var MessageBusInterface */ | |
private $commandBus; | |
public function hireAction(Request $request): Response | |
{ | |
$command = new HireAgency(json_decode($request->getContent(), true)); | |
$this->commandBus->dispatch($command); | |
return new JsonResponse(null, Response::HTTP_CREATED); | |
} | |
} |
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
<?php | |
use Symfony\Component\Validator\Constraints as Assert; | |
final class HireAgency | |
{ | |
/** | |
* @var AgencyId | |
* @Assert\NotNull | |
* @Assert\Uuid | |
*/ | |
private $agencyId; | |
/** | |
* @var string | |
* @Assert\NotNull | |
* @Assert\NotBlank | |
*/ | |
private $name; | |
/** | |
* @var ValueAddedTaxIdentificationNumber | |
* @Assert\NotNull | |
* @Assert\NotBlank | |
*/ | |
private $vatId; | |
/** | |
* @var string | |
* @Assert\Type(type="string") | |
* @Assert\NotNull | |
* @Assert\NotBlank | |
*/ | |
private $contactPerson; | |
/** | |
* @var ContactInformation | |
* @Assert\NotNull | |
* @Assert\Type(type="array") | |
* @Assert\Collection( | |
* fields = { | |
* "email" = { | |
* @Assert\NotBlank(allowNull=true), | |
* @Assert\Email, | |
* }, | |
* "phone" = { | |
* @Assert\NotBlank(allowNull=true), | |
* @Assert\Type(type="string") | |
* }, | |
* "mobile" = { | |
* @Assert\NotBlank(allowNull=true), | |
* @Assert\Type(type="string") | |
* }, | |
* "fax" = { | |
* @Assert\NotBlank(allowNull=true), | |
* @Assert\Type(type="string") | |
* }, | |
* }, allowExtraFields=false | |
* ) | |
*/ | |
private $contactInformation; | |
/** | |
* @var Address | |
* @Assert\NotNull | |
* @Assert\Type(type="array") | |
* @Assert\Collection( | |
* fields = { | |
* "street" = { | |
* @Assert\NotBlank(allowNull=true), | |
* @Assert\Type(type="string"), | |
* }, | |
* "postcode" = { | |
* @Assert\NotBlank(allowNull=true), | |
* @Assert\Type(type="string") | |
* }, | |
* "city" = { | |
* @Assert\NotBlank(allowNull=true), | |
* @Assert\Type(type="string") | |
* }, | |
* "countryCode" = { | |
* @Assert\NotBlank(allowNull=true), | |
* @Assert\Type(type="string") | |
* }, | |
* }, allowExtraFields=false | |
* ) | |
*/ | |
private $address; | |
public function __construct(array $payload) | |
{ | |
$this->agencyId = $payload['agencyId']; | |
$this->name = $payload['name']; | |
$this->vatId = $payload['vatId']; | |
$this->contactPerson = $payload['contactPerson']; | |
$this->contactInformation = $payload['contactInformation']; | |
$this->address = $payload['address']; | |
} | |
public function agencyId(): AgencyId | |
{ | |
return AgencyId::fromString($this->agencyId); | |
} | |
public function name(): string | |
{ | |
return $this->name; | |
} | |
public function vatId(): ValueAddedTaxIdentificationNumber | |
{ | |
return ValueAddedTaxIdentificationNumber::fromString($this->vatId); | |
} | |
public function contactPerson(): string | |
{ | |
return $this->contactPerson; | |
} | |
public function contactInformation(): ContactInformation | |
{ | |
return ContactInformation::fromArray($this->contactInformation); | |
} | |
public function address(): Address | |
{ | |
return Address::fromArray($this->address); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@weaverryan That makes sense. We used to structure commands differently before. We had an additional "payload" attribute that could be validated by the
Collection
constraint. But we wanted to simplify our DTOs. In the end you are right that this is not crucial to the actual application and maybe "over-engineering".Thank you for your feedback!