4
\$\begingroup\$

I would appreciate a thorough review of my updated User entity class. It's still an anemic domain model, but behavior (like upgradeToPremium(), etc) will be added as I'm developing my application further.

This is the first time that I'm commenting my code properly according to the phpDocumenter docs. So some feedback on that would also be highly appreciated.

Any other suggestions are always welcome.


User class:

namespace Model\Domain\User;

use Model\Domain\Entity\Entity;
use Model\Domain\User\Value\Email;
use Model\Domain\User\Value\Token;
use Model\Domain\User\Value\FullName;
use Model\Domain\User\Value\Password;
use Model\Domain\User\Authentication;
use Model\Domain\User\Value\SeriesNumber;
use Model\Domain\User\Value\RegisterDateTime;

/**
 * User entity of the problem domain.
 *
 * @author John Doe <http://google.com>
 */
class User extends Entity
{
    /**
     * @var FullName $fullName A full name value object.
     */
    private $fullName;

    /**
     * @var Email $email An email value object.
     */
    private $email;

    /**
     * @var Password $password A password value object.
     */
    private $password;

    /**
     * @var RegisterDateTime $registerDateTime A register date and time value object.
     */
    private $registerDateTime;

    /**
     * @var array $authentications A list of Authentication child entities.
     */
    private $authentications = [];

    /**
     * Gets the full name value object.
     *
     * @return FullName A FullName instance.
     */
    public function getFullName()
    {
        return $this->fullName;
    }

    /**
     * Sets the FullName instance.
     *
     * @param FullName $fullName A FullName instance.
     *
     * @return self The current User entity.
     */
    public function setFullName(FullName $fullName)
    {
        $this->fullName = $fullName;

        return $this;
    }

    /**
     * Gets the email address.
     *
     * @return string An email address.
     */
    public function getEmail()
    {
        return (string) $this->email;
    }

    /**
     * Sets the Email instance.
     *
     * @param Email $email An Email instance.
     *
     * @return self The current User entity.
     */
    public function setEmail(Email $email)
    {
        $this->email = $email;

        return $this;
    }

    /**
     * Gets the password.
     *
     * @return string A password.
     */
    public function getPassword()
    {
        return (string) $this->password;
    }

    /**
     * Sets the Password instance.
     *
     * @param Password $password A Password instance.
     *
     * @return self The current User entity.
     */
    public function setPassword(Password $password)
    {
        $this->password = $password;

        return $this;
    }

    /**
     * Gets the date and time of registering.
     *
     * @return string A date and time of registering.
     */
    public function getRegisterDateTime()
    {
        return (string) $this->registerDateTime;
    }

    /**
     * Sets the RegisterDateTime instance.
     *
     * @param RegisterDateTime $registerDateTime A RegisterDateTime instance.
     *
     * @return self The current User entity.
     */
    public function setRegisterDateTime(RegisterDateTime $registerDateTime)
    {
        $this->registerDateTime = $registerDateTime;

        return $this;
    }

    /**
     * Assembles an Authentication child entity and adds it to the list.
     *
     * @param integer $seriesNumber     A series number.
     * @param string  $token            A token.
     * @param string  $registerDateTime A date and time of registering.
     *
     * @throws Exception if series number is not unique.
     */
    public function addAuthentication($seriesNumber, $token, $registerDateTime)
    {
        if ($this->findAuthenticationBySeriesNumber($seriesNumber)) {
            throw new Exception('Series number is not unique.');
        }

        $seriesNumber     = new SeriesNumber($seriesNumber);
        $token            = new Token($token);
        $registerDateTime = new RegisterDateTime($registerDateTime);

        $authentication = new Authentication();
        $authentication->setSeriesNumber($seriesNumber)
                       ->setToken($token)
                       ->setRegisterDateTime($registerDateTime);

        $this->authentications[$seriesNumber] = $authentication;
    }

    /**
     * Finds the Authentication child entity with the matching series number.
     *
     * @param string $seriesNumber A series number.
     *
     * @return Authentication|void An Authentication child entity instance if found.
     */
    public function findAuthenticationBySeriesNumber($seriesNumber)
    {
        if (isset($this->authentications[$seriesNumber])) {
            return $this->authentications[$seriesNumber];
        }
    }

    /**
     * Deletes all Authentication child entities.
     */
    public function deleteAllAuthentications()
    {
        $this->authentications = [];
    }

    /**
     * Deletes the Authentication child entity with the matching series number.
     *
     * @param string $seriesNumber A series number.
     */
    public function deleteAuthenticationBySeriesNumber($seriesNumber)
    {
        if (isset($this->authentications[$seriesNumber])) {
            unset($this->authentications[$seriesNumber]);
        }
    }
}

Entity class:

namespace Model\Domain\Entity;

use \Exception;
use Model\Domain\Entity\Value\Identifier;

/**
 * Base class for all entities in the problem domain.
 *
 * @author John Doe <http://google.com>
 */
abstract class Entity
{
    /**
     * @var Identifier $identifier An identifier value object.
     */
    protected $identifier;

    /**
     * Gets the identifier.
     *
     * @return string An identifier.
     */
    public function getIdentifier()
    {
        return (string) $this->identifier;
    }

    /**
     * Sets the Identifier instance.
     *
     * @param Identifier $identifier An Identifier instance.
     *
     * @throws Exception if the identifier is already set.
     *
     * @return self The current entity.
     */
    public function setIdentifier(Identifier $identifier)
    {
        if ($this->identifier) {
            throw new Exception('Identifier is already set and therefore immutable.');
        }

        $this->identifier = $identifier;

        return $this;
    }
}
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I think that your code look good, but since you asked specifically about comments, here are my thoughts.

Comments

  • commenting on getters and setters is a controversial topic. Your getter and setter comments generally don't add any new information. They don't really hurt either, but they can have the effect of a reader ignoring them as well as important comments. It can also have the side effect of you overlooking things that should be commented on in depth.
  • Gets the identifier. and An identifier. in getIdentifier are not actually true. It returns a string representation of the identifier, not the identifier itself. The same is true for many of the other getter (but oddly not for getFullName). This should be expressed in your comments. It should probably also be noted why you are sometimes returning a string and sometimes the identifier object.
  • string $registerDateTime A date and time of registering.: When I have to pass a string (especially a date time string), I'd like to know how it has to be formated.
\$\endgroup\$
3
  • \$\begingroup\$ 1) I've noticed that, but I thought it was a convention. I'm wondering, do you comment your getters and setters? 2) That's correct, I will change those accordingly. Generally, value objects containing more then one value (such as a first and last name) are not casted to a string. 3) The formatting information can be found in the RegisterDateTime class (will throw InvalidArgumentException) and in its setter comment block (of its class). But for the addAuthentication(), yes I'm going to add the expected format in the comment block. Thanks. :) \$\endgroup\$ Sep 16, 2014 at 18:30
  • 1
    \$\begingroup\$ @KidDiamond I used to comment all getter and setters, but right now, I only do it if it adds additional information. If the name doesn't say it all (for example setWidth: the width of what?; setFullName: what's a full name? first name + last name?) and in general if there are restrictions on the parameters or side-effects or anything like that. In your case, I guess an argument could be made that your getters deserve a comment as they cast to string, and your setters because they return the User entity, both of which are somewhat unexpected actions. \$\endgroup\$
    – tim
    Sep 16, 2014 at 20:56
  • \$\begingroup\$ Since I'm using value objects and not just string setters, the class itself will show what values can be retrieved from it by its getters. But I agree that if you're just using string setters, comments would be appropriate to clarify probably by using the @param tags with a description in the comment blocks. As for your last point, I think it would be nice if I added in the comments that the setters return self to allow the fluent interface implementation. Since I'm new to commenting like this I need to get used to it for a bit, to verbally express what is being done and why. \$\endgroup\$ Sep 16, 2014 at 21:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.