This is my demonstration case:
<?php
declare(strict_types=1);
final class Order
{
/** #var array|OrderPosition[] */
private $orderPositions;
public static function fromArray($orderData)
{
assert(isset($orderData['orderId']));
assert(isset($orderData['positions']));
return
new self(
$orderData['orderId'],
array_map(
function (array $positionData): OrderPosition {
// I would like to put the "future self (Order)" alread here
return OrderPosition::fromArray($positionData);
},
$orderData['positions']
)
);
}
private function __construct(string $orderId, array $orderPositions)
{
$this->orderPositions = $orderPositions;
// what I want to avoid is:
array_walk($orderPositions, function (OrderPosition $position) {
$position->defineOwningOrder($this);
});
}
}
final class OrderPosition
{
/** #var Order */
private $owningOrder;
public static function fromArray($positionData /* + as mentioned I'd like to put the "calling" Order here already...? */)
{
return
new self(
$positionData['productId'],
$positionData['amount']
);
}
private function __construct(string $productId, int $amount)
{
// …
}
/** #internal */
public function defineOwningOrder(Order $order)
{
$this->owningOrder = $order;
}
}
I like to have a pointer to the "parent"/owning Order item in my OrderPosition; however since Order is considered an Aggregate Root I want Order to be in charge of creating the collection of OrderPositions.
How should I put the Order item in every OrderPosition when the final Order is not yet there on creation?
My current approach is to late-set it in Order's ctor but that would mutate OrderPosition, strictly spoken.
You have a combination of several design decisions here which are in conflict:
immutable objects
a circular reference
a constructor which is not responsible for constructing the dependent objects
a factory method which is not allowed to see and mutate a partial object
As you say, your current implementation compromises on (1) by allowing the OrderPosition to have the extra reference added in later.
You can make the problem go away if you remove (2). What is the situation where you would have a reference to an OrderPosition and want to navigate to the Order to which it belongs? Can that situation be re-framed as a responsibility of the Order, removing the circular reference?
You could change (3) such that the constructor took the information to create OrderPositions, not the OrderPositions themselves. In your example, this would be trivial, but if in practise you have a number of different factories feeding into one constructor, this might become messy.
Alternatively, if you relax (4) you could pass the partially constructed object into the OrderPosition constructor / factory:
public static function fromArray($orderData)
{
assert(isset($orderData['orderId']));
assert(isset($orderData['positions']));
$instance = new self($orderData['orderId']);
foreach ( $orderData['positions'] as $positionData ) {
$instance->orderPositions[] = OrderPosition::fromArray($positionData, $instance);
}
return $instance;
}
While this still looks like mutation, it is only the same mutation you would do in any constructor - you are creating the initial state of the object.
In a language that supported overloaded or named constructors, fromArray would be a constructor, and might not share any implementation with other constructors. In PHP, you can emulate that pattern with an empty private function __construct(){} and static methods starting with $instance = new self;
Related
I've a class, Proposal, which has a $status of type ProposalStatus. Now, for the most part the ProposalStatus's identity does not change... but it does (sort of). It's $id is fixed, but the $display_name and $definition (just strings) can change over a long period of time as the master data is updated, but it will NOT change within the lifetime of an HTTP Request-Response.
Question #1 - Entity or Value Object?
Is a value object something is never supposed to change or only never supposed to change over the lifetime of a specific execution of the application? If the display name or definition are changed then really I expect / want it to be changed for everyone. However, since they can be defined outside of the proposal I'm think that just straight up makes them entities instead of value objects.
At no time does the Proposal change the ProposalStatus's attributes, it only changes which ProposalStatus is has.
Question #2 - How to set the status correctly for a domain-driven design?
My proposal object has the ability to manage it's statuses, but in order to do that I need to have a specific ProposalStatus object. Only, where is the list of statuses that allows it to returns the right expected to be?
I could get it from a the ProposalRepository... but everything is accessed via the aggregate root which the Proposal so that doesn't make sense.
I could have constants that match the $id of the ProposalStatus, but that seems wrong.
I could make a ProposalStatusRepository... but should I be accessing another repository from within the Proposal?
I could make a array of all possible statuses with the $id as the key and add to the proposal, but that isn't much different from a repository...
Example:
class ProposalStatus {
protected $id; // E.g., pending_customer_approval
protected $display_name; // E.g., Pending Customer Approval
protected $definition; // E.g., The proposal needs to be approved by the customer
}
class Proposal {
/**
* The current status of the proposal
* #var ProposalStatus
*/
protected $proposal_status;
public function withdraw() {
// verify status is not closed or canceled
// change status to draft
}
public function submit() {
// verify status is draft
// change status to pending customer approval
}
public function approve() {
// verify status is pending customer approval
// change status to approved
}
public function reject() {
// verify status is pending customer approval
// change status to rejected
}
public function close() {
// verify status is not canceled
// change status to closed
}
public function cancel() {
// verify status is not closed
// change status to canceled
}
}
From what I understand from your domain, ProposalStatus should be a Value object. So, it should be made immutable and contain specific behavior. In your case, the behavior is testing for a specific value and initializing only to permitted range of values. You could use a PHP class, with a private constructor and static factory methods.
/**
* ProposalStatus is a Value Object
*/
class ProposalStatus
{
private const DRAFT = 1;
private const PENDING_CUSTOMER_APPROVAL = 2;
private const CANCELLED = 3;
private const CLOSED = 4;
/** #var int */
private $primitiveStatus;
private function __construct(int $primitiveStatus)
{
$this->primitiveStatus = $primitiveStatus;
}
private function equals(self $another): bool
{
return $this->primitiveStatus === $another->primitiveStatus;
}
public static function draft(): self
{
return new static(self::DRAFT);
}
public function isDraft(): bool
{
return $this->equals(static::draft());
}
public static function pendingCustomerApproval(): self
{
return new static(self::PENDING_CUSTOMER_APPROVAL);
}
public function isPendingCustomerApproval(): bool
{
return $this->equals(static::pendingCustomerApproval());
}
public static function cancelled(): self
{
return new static(static::CANCELLED);
}
public function isCancelled(): bool
{
return $this->equals(static::cancelled());
}
public static function closed(): self
{
return new static(static::CLOSED);
}
public function isClosed(): bool
{
return $this->equals(static::closed());
}
}
class Proposal
{
/** #var ProposalStatus */
private $status;
public function __construct()
{
$this->status = ProposalStatus::draft();
}
public function withdraw()
{
if (!$this->status->isClosed() && !$this->status->isCancelled()) {
$this->status = ProposalStatus::draft();
}
}
// and so on...
}
Note that immutability is an important characteristic of a Value object.
In case that your ProposalStatus is a fixed list of values just go for the enumeration approach.
Otherwise you need to treat ProposalStatus as an AggregateRoot that users can create, update and delete (I guess). When assigning a ProposalStatus to a Proposal you just need the ID. If you want to check that the given ID exists you just need to satisfy the invariant with a specialized query. Specification pattern fits well here.
class ProposalStatusExistsSpecification
{
public function isSatisfiedBy(string $proposalSatusId): bool
{
//database query to see if the given ID exists
}
}
You can find here the Interfaces to implement your specification.
Is list of all possible proposal statuses static? I think it is. So ProposalStatus looks like a simple enumeration. Attributes like DisplayName and Definition are not related to business code.
Just define ProposalStatus as enumeration (static class with read-only fields or any other structure supported by your language). It shuld be defined in business layer. Bussiness code should be able to distinguish enumeration values (e.g. if (proposal.Status == ProposalStatus.Pending) { poposal.Status = ProposalStatus.Approved; }).
In application or even presentation layer define a dictionary that contains DisplayName and Definition mapped to ProposalStatus. It will be used only when displaying data to users.
In my PHP application I have a base class for all database objects, which is further extended by specific model classes. It goes on like this (simplified):
abstract class Model extends Collection {
(...)
function __construct(string $primary_key, string $value) {
$data = MysqlDB::instance() -> select(static::TABLE, implode(',', static::COLUMNS), "$primary_key=\"$value\"");
if (count($data) != 1)
throw new ModelException('Select condition uncertain');
parent::__construct($data[0]);
}
public static function getById(string $id) : ?Model {
try {
return new static('id', $id);
} catch (ModelException $e) {
return NULL;
}
}
(...)
}
The point is, I use the getById function on child classes to obtain the needed objects. However, since the return type of getById method is Model, PhpStorm can't figure out what methods the objects has and highlights the ones I use as an error. Consequently, no type-hinting available.
For instance, assuming that final class User extends Model and that class User has method sendNotification, this kind of code:
User::getById($id) -> sendNotification($param1, $param2, ...)
has sendNotification yellowed out as though it did not exist.
I know this isn't that much of an issue, but it's really irritating in terms of that I get distracted by constant redundant warnings and that I can't use type-hinting in such a usage case.
Try with actual PHPDoc for your getById() where you specify dynamic type (e.g. #return static or #return $this).
That's the most common way of providing such "this class and its' children" typehint.
Another possible option is kind of the same .. but using PHP 7.1 functionality (so no PHPDoc blocks).
Try using self as return type instead of Model. I mean here:
public static function getById(string $id) : ?Model {
use this instead:
public static function getById(string $id) : ?self {
But for this one you should use PhpStorm 2017.2 EAP build as 2017.1.x has issues with that (see https://stackoverflow.com/a/44806801/783119 as an example).
There are three ways to achieve this, none of them has anything to do with PHPStorm.
First overwrite the method in every child with the proper return type:
/**
* #return User
*/
public static function getById(string $id) : ?Model { return parent::getById($id); }
Second add all possible children to the return tag of the abstract class.
/**
* #return Model|User|...
*/
public static function getById(string $id) : ?Model { /* ... */ }
Third add a type hint just in place:
/** #var User $user */
$user = User::getById($id);
$user->sendNotification($param1, $param2, ...);
These are not nice and hard to maintain. But there is no "setting" in PHPStorm for that.
In the progress of writing a little framework for a web app I came along some difficulties in making classes communicate with each other.
Environment
I have an abstract class called LizardModule, that should be extended by all the single modules of the web-app. This class has a final protected function registerController(...), that creates a new Object of the type LizardController. This is, as it sounds, based on the idea of MVC. With the final protected function registerFunction(...), modules can register functions for every controller. Those are stored using addFunction(...) on the controller object. Here is what this looks like:
Example Module:
class ModuleOverview extends LizardModule {
protected function setup() {
$this->registerController(
'overview',
'App Overview'
);
$this->registerFunction(
'overview',
'myfunction',
'My Function',
array(&$this, 'theFunctionToCall')
);
}
public function theFunctionToCall() { ... Generate Content ... }
}
Module Class:
class LizardModule {
private $controllers = array();
final public function __construct() { $this->setup(); }
abstract protected function setup();
[...]
final protected function registerController($controllerSlug, $controllerName) {
if (array_key_exists($controllerSlug, $this->controllers))
return false;
$this->controllers[$controllerSlug] = new LizardController($controllerSlug, $controllerName);
}
final protected function registerFunction($controllerSlug, $functionSlug, $functionName, callable $function) {
if (!array_key_exists($controllerSlug, $this->controllers))
return false;
$this->controllers[$controllerSlug]->addFunction($functionSlug, $functionName, $function);
}
}
This results in a lot of objects of type LizardController in different places of the app. To make all of those objects accessable, I created a singleton class LizardRouter, that should hold a reference to all of those controller objects. Therefore, the controller-object registers itself with this singleton class:
Controller Class:
class LizardController {
[...]
private $functions = array();
public function __construct($slug, $name, $menu) {
$this->slug = $slug;
$this->name = $name;
$this->menu = $menu;
LizardRouter::registerController($this);
}
public function addFunction(...) { Tested, this works. }
public function getFunctions() {
return $this->functions;
}
}
Router Class:
final class LizardRouter {
[...]
public static function getControllers() {
return static::getInstance()->controllers;
}
public static function registerController(LizardController $controller) {
static::getInstance()->controllers[] = $controller;
}
}
The Problem
The whole thing works alright for the controllers. In my interface class, I can read out all controllers and print a menu containing their names. The problem is: Whenever I access the controllers functions-array (see controller class) through the controllers-array given by the routing class, I get an empty array. I asume that somewhere a reference is not working and I am passing the actual controller object, before my module-class was able to add the functions to the controllers functions-array. But I can't figure out where exactly the problem lies. Here is an example from my interface class showing the problem:
foreach (LizardRouter::getControllers() as $controller) {
// Allways returns an empty array, even though
// the module added functions to the controller.
$controller->getFunctions();
}
Since this is a very specific case, I guess it is unlikely, that anyone will ever stumble upon the same problem. Anyway; I found the reason for the problem:
Objects are by default passed as reference since PHP5. Variables are by default passed by value.
Arrays are handled like variables, so when I pass an array containing object-references, a new copy of this array is created and passed. Object references added to the array after it was passed are therefore only added to the original array.
The solution i chose was to create my own "array-class" for holding objects. It has nothing more than a private array object, a setter and a getter. Since this custom array class is an object, it is automatically passed by reference. Later I also added some functions to conveniently access the array - a good side-effect.
I have a class with 23 different properties of various types, but I'll only show three in this example. A typical way to construct this class would be:
class Item
{
public $name;
public $price;
public $date;
// Getters and setters
public function get_name() {return $name;}
public function get_price() {return $price;}
public function get_date() {return $date;}
public function set_name($val) {$name = $val;}
public function set_price($val) {$price = $val;}
public function set_date($val) {$date = $val;}
}
With 23 different properties I would need 46 different set/get functions, and my code would have to call all these different functions by specific name, so it's difficult to create loops.
Can I do this instead:
class Item
{
public $props = array (
'name' => NULL,
'price' => NULL,
'date' => NULL
);
// Getters and setters
public function get( $key ) { return $props[$key]; }
public function set( $key, $val ) { $props[$key] = $val; }
public function getKeys() { return array_keys( $props ); }
}
To get a property such as $name I'd simply use Item->get('name');
rather than the specific getter function Item->get_name();
This way, even if I have 23 different properties of different types, I still use only one getter and one setter for all of them. Plus, it's easy to loop through all (or a subset) of the properties by doing a foreach on the getKeys() array.
It seems so convenient I would think it would be a pretty standard format for PHP classes, yet in all the tutorials and examples of PHP classes that I've seen I have never seen this construct used. Is there a problem with it?
This is fine if you don't care what type your properties are or you don't care if they should be restricted to a certain set of known properties (ie, you don't care about encapsulation).
If however you want to use specific types, you will need some control.
class Foo {
/**
* #var Bar
*/
private $bar;
public function setBar(Bar $bar) {
$this->bar = $bar;
}
/**
* #return Bar
*/
public function getBar() {
return $this->bar;
}
}
The other real benefit to setters in particular is the ability to perform transformations on input data. For example
class Foo {
/**
* #var DateTime
*/
private $date;
public function setDate($date) {
if (!$date instanceof DateTime) {
$date = new DateTime((string) $date);
}
$this->date = $date;
}
}
it's difficult to create loops.
What meaningful loop would you make over name, price and date?
The reason individual getters and setters are used is encapsulation: prohibiting things outside the class insight into its insides. For example, say you have age property. Then you extend your class with birth_date property. Then you think it would be a good idea to automatically calculate age instead of having to assign both values. If you have a getter for age, it is trivial: just have it stop returning $props["age"], and start returning the difference between the birth date and now. But if you're not using the age getter but a direct access, you would have to change every single place in your codebase where you're accessing age.
Or say that you want to issue a log statement every time a property changes. Trivial to put it inside a setter, but you're screwed if you have a public property that anyone is allowed to mess with at will.
I have a record class with 18 properties.
Before that class can be submitted to the database, all 18 properties must have validated data.
Because I'm OOP-ifying a working procedural webapp, I went about this sort of backwards.
First I addressed workflow for modifying existing records. At the time, it made sense to throw all 18 properties into the __construct method and avoid craploads of setters. A separate loader class handles the dbase business and can return either single objects or an array of record objects. That all worked ok.
But then it came time to address the new record creation workflow, and suddenly I needed to instantiate an empty record, except my record constructor is a hungry beast that wants 18 parameters...
...so you strip the constructor? But then I'd have to add 18 setters and call them all each time I want to work with an existing record...
doesn't seem like much of an improvement! :-/
How do real programmers handle this? (I'm just a weenie hobbyist...)
Either default arguments is one option, but then you have to fill out a large number of null's if you only want to use, say, the first and last.
Then again, you could do array looping:
private $prop1;
private $prop2;
// more properties here.
function __construct( array $props ) // `array` here is for type-hinting.
{
foreach( array( 'prop1', 'prop2' /*, all of the props for this object */
as $property )
{
// basically, this will assign all of the properties as they exist in the
// props array
if( isset( $props[ $property ] ) )
$this->$property = $props[ $property ];
}
}
Or, if you wanted to keep your old constructor signature:
function __construct( $prop1, $prop2 = NULL, $prop3 = NULL /* ... */ )
{
if( is_array( $prop1 ) )
{
$this->array_prop_assignment( $prop1 );
}
else
{
$args = func_get_args();
// this ensures that anything which is passed to the constructor
// will go to the "new" old constructor
call_user_func_array( array( $this, 'param_prop_assignment' ), $args );
}
}
function param_prop_assignment( $prop1, $prop2 /* ... */ )
{
//your old constructor can go here.
}
function array_prop_assignment( array $props )
{
// foreach example above would go here.
}
The new version also gives you the option to simply:
$k = new DataClass(); // no idea what the real class name is.
$k->param_prop_assignment( 1, 2, 3 /* ... */ );
Stuffing 18 parameters in any sort of function is (constructor included) is bad. You'll never remember the correct order when you look at your code few months, or even few days from now on. Further more, as you experienced, when you need to extend the class it's hard.
That's why I usually prefer having classes with getters and setters. Yes it's more typing, but with getter and setters users can easily see what properties they can get and set, plus getter and setters are IDE's auto-complete friendly.
Now, the next problem is you don't want to call setters all the time when you read existing record from the database? You said you have a Loader class, can't you just centralize all the calls to setters in the Loder class?
class Loader{
public function getMyObject(){
$dbData = $this->getDataFromDB();
$myObj = $this->createMyObjectFromDbData($dbData);
return $myObj;
}
private createMyObjectFromDbData($dbData){
$myObj = new MyObject();
/* 18 setter calls */
return $myObj;
}
}
So when you want to work with existing you can simply call Loader.getMyObject();
If you don't feel like typing out all the 18 setter calls in createMyObjectFromDbData, then as long as your setter follows some naming convention you can do
for ($dbData as $columnName => $columnValue){
$propertyName = $columnName;
$propertyValue = $columnValue;
$myObj->set{$columnName}($propertyValue); /* you can't do this in java */
}
You may also want to add a validate method to validate all the properties in the object so you can call that method before you submit it to be inserted into database.
You can chain them to the constructor. Where you do this ...
$Record = Record()->Name('Mark')->Location('A-Town, NY')->Phone('123-345-6789');
You can do this by making a function that has the same name as your class that returns a new instances of your class.
function Record() {
return new Record;
}
class Record {
private $Name;
private $Location;
private $Phone;
public function __get($property) {
return (isset($this->$property)) ? $this->$property : FALSE;
}
public function &__call($property, $arguments)
{
if (property_exists($this, $property))
$this->$property = array_shift($arguments);
return $this;
}
}
$FilledRecord = Record()->Name('Mark')->Location('A-Town')->Phone('123-456-7890');
$EmptyRecord = Record();
print_r($FilledRecord);
print_r($EmptyRecord);
If you need to validate some data, you can just add the function on later.
In a perfect world your object would allow you to specify values using either the constructor or setters. What you could do to simplify things is provide a constructor that only accepts a subset of the 18 values and sets the rest to default values. But that assumes that there are default values that make sense, which may not be the case.
If you have a lot of properties for an object and you don't want to include them all in the constructor, you can use setters return the object itself to make slightly more readable code:
MyClass obj = (new MyClass())
.setName(name)
.setLocation(location)
...
.setPhone(phone)
Along the same lines, if all of the values need to be validated before you have a valid object, you can use a builder object to do the same thing. Basically, you create a builder object, set the values, and then tell the builder to create the actual object. It can then do the validation of all the values (including validation that uses multiple fields) right before it constructs the actual instance of the object.
MyClass obj = (new MyClassFactory())
.setName(name)
.setLocation(location)
...
.setPhone(phone)
.construct();