@bestmomo @cm @JarekTkaczyk @opheliadesign
I know I'm preaching to the choir here, but I don't know why you think this is more complex? It is OO programming with reusable code. Agree that is not well suited to smaller applications, but if you are working on a med to large app predictability and repeatability are vital. Also if you just write like this always then there is some overhead in setup but you make up for itt on the backend.
Jarek, sorry it was 2 am and I should never write code that late. In fact I should just never write code :)
Btw, CM, loved the ninja turtle diagram, but the analogy is slightly wrong :) In OOP it would be extract 4 DNA samples from from the same turtle put into 4 drums of a mutation slime you end up with 4 different ninja turtles. Same DNA from a single turtle, 4 different ninja turtles. If you inject the DNA, in this case the mobile phone number into different value objects (drums of slime), you will get different ninja turtles i.e. value objects. The object, or turtle, will contain the mutated value not the drum or DNA. Once you inject the mobile phone number in any amount of value objects the phone number is nolonger the number but a mutated version i.e. value objects will take that value and create a different instance in this case mobile phone and receive, but you could have n generated with the same value. i.e
$Leonardo->swordStrike()
$michelangelo->staffStrike(); // or whatever his weapon is
$raphael->ninjaStarStrike();
//not
$drum->kickass();
$turtle->kickass();
$DNA->kickass();
(man I stretched that analogy as far as it would go )
Here is the example. I'm not going to add the value objects back. They need some tweaks, but the concept remains the same so reference above.
In your controller, command or registration service you instantiate the value objects. The value object (not included in this post, but in my previous) are basic string examples, but you could create a phone type, Boolean type, etc. Yes I could add the magic methods as abstract class and make the code more efficient, but stick with the concept for a second.
Once I have a value object setup I can use the input $mobilePhoneNumber as the common denominator for any value object phone number dependencies that will mutate the number into some other value. In this case two, but like I said you could have n.
// let say in my controller I would get the $phoneNumber and pass it to the VO
$mobilePhoneNumber = $request->only('mobile_number');
$setMobile = new MobilePhone($mobilePhoneNumber);
$setReceive = new ReceiveSms($mobilePhoneNumber);
// but could have as many of these as I need
$setYetAnotherDependency = new ValueObject($mobilePhoneNumber);
- Inside of my model I could have a registration method where I would inject these. Let's say a registration method
// a registration method as an example
// passing the two values but you could inject other username, email, etc
User::register($setMobile, $setReceive, $setYetAnotherDependency);
public function register(MobilePhone $mobileNumber, ReceiveSms $setReceive, ValueObject $setYetAnotherDependency)
{
$user = new User();
$user->mobile = $mobileNumber;
$user->receive = $setReceive;
$user->value = $setYetAnotherDependency;
return $user;
}
- Inside of the same model I would have a mutators for each of my dependencies
protected function setMobileAttribute(MobilePhone $mobile )
{
$this->attributes['mobile'] = $mobile->toString();
}
protected function setReceiveAttribute(ReceiveSms $receive )
{
$this->attributes['receive'] = $receive->toString();
}
protected function setAnotherMobileAttribute(ValueObject $value )
{
$this->attributes['value'] = $value->toString();
}
The advantage of this approach is I now have Value Objects for mobile phone and receive, etc. that I can inject anywhere in my application.
Simple example as I cannot think of better one :) A specification pattern to check for international numbers.
class PhoneNumberIsInternational implements PhoneNumberSpecification
{
/**
* @var UserRepository
*/
private $repository;
public function __construct(UserRepository $repository)
{
$this->repository = $repository;
}
/**
* Check to see if the specification is satisfied
*
* @param MobilePhone $mobile
* @return bool
*/
public function isSatisfiedBy(MobilePhone $mobile)
{
// some regex that checks for international numbers in user or other repo
if ($this->repository->phoneNumberIsInternational($mobile))
{
return true;
}
return false;
}
}
I could also use these in my validation
class ExampleRegisterRequest extends Request {
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
// simple example without any check etc.
$setRule = new MobilePhone( self::checkPhoneIsInternational(Request::get('mobile_number')));
return [
'mobile_phone' => $setRule,
];
}
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return Auth::check();
}
private static function checkPhoneIsInternational(MobilePhone $mobileNumber)
{
$specification = new PhoneNumberIsInternational($mobileNumber);
if($specification->isSatisfiedBy($mobileNumber)) {
return 'international regex ';
}
else
{
return 'us phone regex';
}
}
}
A lot more than just mutator, but that was my point in an a 1000 word description :)