I've got one Aggregate root - Product - that has few fields and some of them are objects, like Price. It looks like that ( of course it is simplified ):
Product
{
private $price;
public function __construct(Price $price)
{
$this->price = $price;
}
}
Price
{
private $currency;
private $amount;
public function __construct($currency, $amount)
{
$this->currency = $currency;
$this->amount= $amount;
}
}
This aggregate root doesn't contain "getPrice" method, it's not needed in Domain.
The issue:
I need to serialize this aggregate, but I would like to have it in this format:
Product.json
{
"priceCurrency": "GBP",
"priceAmount": 100
}
I've been trying with JMSSerializer, but can't really get it from config. This for example doesn't work:
Product.yml
Namespace\Product:
virtual_properties:
getAmount:
exp: object.price.amount
serialized_name: priceAmount
type: integer
getCurrency:
exp: object.price.currency
serialized_name: priceCurrency
type: string
I understand that it's due to the fact, that "exp" part is being used by Symfony Expression Language and from what I know it doesn't support getting values from private fields in any other way then through theirs methods. I also know that JMSSerializer itself supports that. I don't have to have field "getPrice" to serialize "price" field.
Question: Is there any way to achieve what I want just through config or do I have to write listeners on post_serialize event?
Use something like this:
<?php
class Property
{
protected $reflection;
protected $obj;
public function __construct($obj)
{
$this->obj = $obj;
$this->reflection = new ReflectionObject($obj);
}
public function set($name, $value)
{
$this->getProperty($name)->setValue($this->obj, $value);
}
public function get($name)
{
return $this->getProperty($name)->getValue($this->obj);
}
protected function getProperty($name)
{
$property = $this->reflection->getProperty($name);
$property->setAccessible(true);
return $property;
}
}
// example
class Foo
{
protected $bar = 123;
public function getBar()
{
return $this->bar;
}
}
$foo = new Foo();
echo 'original: '.$foo->getBar().PHP_EOL;
$prop = new Property($foo);
echo 'reflection - before changes: '.$prop->get('bar').PHP_EOL;
$prop->set('bar', 'abc');
echo 'after changes: '.$foo->getBar().PHP_EOL;
Related
I've been doing a project in PHP for the last few hours and I have encountered into a problem.
The problem is I don't know how to access private variables in a class and I can't find it online.
Example:
<?php
class Example{
private $age;
public function __construct() {
$age = 14;
$this->checkAge();
}
private function checkAge() {
if($this->$age > 12)
echo "welcome!";
}
}
$boy = new Example();
?>
As far as I know, I should be able to access the variable with $this->$age but it isn't working.
Thank you.
EDIT: Got it working with help of the awesome stackoverflooooooooow community, this is how a working one looks.
<?php
class Example{
private $age;
public function __construct() {
$this->age = 14;
$this->checkAge();
}
private function checkAge() {
if($this->age > 12)
echo "welcome!";
}
}
$boy = new Example();
?>
Look at this approach.
first: create Entity that stores and retrieves data inside of private $attributes array, and with magic __set(), __get() You can also do like: $object->variable = 123
second: extend Entity with Human class and add some function specific to child class (for example hasValidAge()):
<?php
class Entity {
private $attributes;
public function __construct($attributes = []) {
$this->setAttributes($attributes);
}
public function setAttribute($key, $value) {
$this->attributes[$key] = $value;
return $this;
}
public function setAttributes($attributes = []) {
foreach($attributes AS $key => $value) {
$this->setAttribute($key, $value);
}
}
public function getAttribute($key, $fallback = null) {
return (isset($this->attributes[$key]))?
$this->attributes[$key] : $fallback;
}
public function __get($key) {
return $this->getAttribute($key);
}
public function __set($key, $value) {
$this->setAttribute($key, $value);
}
}
class Human extends Entity {
public function __construct($attributes = []) {
$this->setAttributes($attributes);
$this->checkAge();
}
public function hasValidAge() {
return ($this->getAttribute('age') > 12)? true : false;
}
}
$boy = new Human(['name' => 'Mark', 'age' => 14]);
if($boy->hasValidAge()) {
echo "Welcome ".$boy->name."!";
}
?>
p.s. I've removed echo "Welcome!" part from constructor because it's not cool to do echo from model object, in our example Human is model of Entity.
In PHP, I have a product object that contains a collection of attributes. json_encode produces this:
{"id":"123","name":"abc","attributes":{"attributes":[{"key":"sku","value":"xyz"}]}}
"attributes" listed twice is redundant. What's the best way of structuring object collections so that the json is clean?
class Product {
public $id;
public $name;
public $attributes;
public function __construct()
{
$this->attributes = new Attributes();
}
public function get($id)
{
$this->id = "123";
$this->name = "abc";
$attribute = new Attribute("sku", "xyz");
$this->attributes->add($attribute);
}
}
class Attributes
{
public $attributes;
public function __construct()
{
$this->attributes = array();
}
public function add($attribute)
{
array_push($this->attributes, $attribute);
}
}
class Attribute
{
public $key;
public $value;
public function __construct($key, $value)
{
$this->set($key, $value);
}
}
I would just use an associative array.
class Product {
...
public $attributes=array();
...
public function get($id)
{
...
$this->attributes["sku"]="xyz";
$this->attributes["foo"]="bar";
}
}
json_encode() should produce something like this:
{"id":"123","name":"abc","attributes":{"sku":"xyz","foo":"bar"}}
OR using variable variables:
class Attributes
{
public function add($key,$value)
{
$this->{$key}=$value;
}
public function drop($key)
{
unset($this->{$key});
}
}
$a=new Attributes();
$a->add('sku','xyz');
$a->add('foo','bar');
echo json_encode($a).'<br>';
$a->drop('sku');
echo json_encode($a).'<br>';
Output:
{"sku":"xyz","foo":"bar"}
{"foo":"bar"}
You can give your classes a custom json encoding format by implementing JsonSerializable.
In your case you'll just need to have Attributes implement that and give it a jsonSerialize method which returns $this->attributes.
I have been reading Rafactoring by Martin Fowler and in the beginning of the book he uses an example application (written in Java), which I am trying to convert over to PHP for training purposes. (I have trimmed the code down to make this question, but it works if the variables are public.)
The trouble is to create a statement I need access to a value (see switch) using method getCode() of Movie class since $code is private. (Of course if all of the variables were public the code below would work, but I want to keep them private.)
Can someone please shed some light on how I would access the private variable calling the getCode() method of Movie from the switch in statement(). (Or if there is a better way to do it, let me know.)
class Movie {
private $title;
private $code;
public function __construct($title, $code) {
$this->title = $title;
$this->code = $code;
}
public function getCode() {
return $this->code;
}
public function getTitle() {
return $this->title;
}
}
class Rental {
private $movie; // will carry a Movie object
private $days;
public function __construct(Movie $movie, $days) {
$this->movie = $movie;
$this->days = $days;
}
public function getMovie() {
return $this->movie;
}
}
class Customer {
private $name;
private $rentals; // will be a collection of Rental Objects
public function __construct($name) {
$this->name = $name;
}
public function addRental(Rental $rental) {
$this->rentals[] = $rental;
}
public function statement() {
$thisAmount = 0;
foreach ($this->rentals as $each) {
// what is the better way to call this value??????
switch ($each->movie->code) {
case 1:
$thisAmount+= ($each->days - 2) * 1.5;
break;
case 2:
$thisAmount += $each->days * 3;
break;
case 3:
$thisAmount += 1.5;
break;
}
// show figures for this rental
$result = "\t" . $each->movie->title . "\t" . $thisAmount . "\n";
}
return $result;
}
}
// pick a movie
$movie = new Movie('Star Wars', 0);
// now rent it
$rental = new Rental($movie, '2');
// now get statement
$customer = new Customer('Joe');
$customer->addRental($rental);
echo $customer->statement();
You are iterating over a collection of movie in your foreach. So you can do it this way:
foreach($this->rentals as $rental) {
switch($rental->getMovie()->getCode()) {
Of course, you can leave your variable named each. I just find $movie more readable and understandable in this context.
replace your line with just:
$each->getMovie->getCode()
Following "problem"
PHP Class with a lot of propertys. A lot of Getters / Setter.
Is there any nice solution to convert all propertys to an array?
protected $name;
protected $date;
public function getName();
public function getDate();
public function asArray(); // call all getters?
Is your API already defined and are you stuck with getX and setX methods? I much prefer properties. Less typing, better distinction between properties and methods, and resulting code looks more like PHP and less like Java. But exposing properties doesn't mean you lose encapsulation and make all your internals public. With __get and __set magic methods you can have pretty fine-grained control over what you present. Plus, it would be rather trivial to dump the properties as an array:
class Foo
{
protected $properties;
public function __construct() {
$this->properties = array();
}
public function __set($prop, $value) {
$this->properties[$prop] = $value;
}
public function __get($prop) {
return $this->properties[$prop];
}
public function toArray() {
return $this->properties;
}
}
Alas, if you're stuck with setters/getters because of a cranky boss or some misunderstanding of what OOP must be, why not just cast the object to an array?
class Bar
{
public $x;
public $y;
public $z;
protected $a;
protected $b;
protected $c;
private $q;
private $r;
private $s;
public function __construct() {
}
public function setA($value) {
$this->a = $value;
}
public function getA() {
return $this->a;
}
public function setB($value) {
$this->b = $value;
}
public function getB() {
return $this->b;
}
public function setC($value) {
$this->c = $value;
}
public function getC() {
return $this->c;
}
public function toArray() {
return (array)$this;
}
}
Notice how public, protected, and private properties are cast:
$bar = new Bar();
print_r($bar->toArray());
array(9) {
["x"]=>
NULL
["y"]=>
NULL
["z"]=>
NULL
[" * a"]=>
NULL
[" * b"]=>
NULL
[" * c"]=>
NULL
[" Foo q"]=>
NULL
[" Foo r"]=>
NULL
[" Foo s"]=>
NULL
}
Note that the array keys for protected/private don't start with a space, it's a null. You can re-key them, or even filter out protected/private properties if you like:
public function toArray() {
$props = array();
foreach ((array)$this as $key => $value) {
if ($key[0] != "\0") {
$props[$key] = $value;
}
}
return $props;
}
You're working with a dynamic language; take advantage of that and enjoy it!
How about using ReflectionClass and ReflectionMethod, something like this:
class PropertyHolder
{
private $name;
private $date;
private $anotherProperty;
public function __construct($name, $date)
{
$this->name = $name;
$this->date = $date;
}
public function getName()
{
return $this->name;
}
public function getDate()
{
return $this->date;
}
public function asArray()
{
$result = array();
$clazz = new ReflectionClass(__CLASS__);
foreach ($clazz->getMethods() as $method) {
if (substr($method->name, 0, 3) == 'get') {
$propName = strtolower(substr($method->name, 3, 1)) . substr($method->name, 4);
$result[$propName] = $method->invoke($this);
}
}
return $result;
}
You could use PHP's reflection capabilities. Here's an example:
http://www.weberdev.com/get_example-4672.html
Try looking into get_object_vars(), get_class_vars and others in the same category. The examples shown there look like pretty much what you need. Check the comments there (for example http://www.php.net/manual/en/function.get-class-vars.php#87772) they already provide ways that suit your needs.
A simple (array) cast on $this will suffice:
(array) $this;
If you have additional properties (private ones for example, that shall not be toArray()ed) you can afterwards unset these:
public function toArray() {
$array = (array) $this;
unset($array['private'], $array['privateagain']);
return $array;
}
One option would be to create an array in your constructor.
You will have one getter and one setter..
When you want to set or get something, do something like:
$foo->get( 'UID' ); //(to get user id)
or
$foo->set( 'UID', 5 ); // to set something)
I'm currently studying OOP Method Chaining, but I'm having difficulties from making it work.
I've created setValue method where it returns a value based on the parameters.
Then, I've created setLabel method and added a parameter to be used after a setValue has been called.
Here is the current code I have:-
class Field
{
public $label;
public function setValue($property, $value)
{
$this->$property = new \stdClass();
$this->$property->value = $value;
$this->$property->label = $this->label;
return $this;
}
public function setLabel($label)
{
return $this->label = $label;
}
}
A sample code for retrieving the data:-
$field = new Field;
$field->setValue('textfield', 'something to print')->setLabel('Label here');
print $field->textfield->value; // Returns the value
print $field->textfield->label; // Returns an empty string
I can't figure out what's wrong with my code. Please help.
Assuming that the following is your goal:
print $field->textfield->value; // Returns the value
print $field->textfield->label; // Returns the label
if you want this to work:
$field = new Field;
$field->setValue('textfield', 'something to print')->setLabel('Label here');
Then you need to do something like this:
class FieldProperty {
public $label
public $value;
public setValue($value) { $this->value = $value; return $this; }
public setLabel($label) { $this->label = $label; return $this; }
}
class Field
{
public function setValue($property, $value)
{
$this->$property = new FieldProperty();
$this->$property->setValue($value);
return $this->$property;
}
}
Thinking about it further, we can add:
class Field
{
public function setValue($property, $value)
{
$this->$property = new FieldProperty();
$this->$property->setValue($value);
return $this->$property;
}
public function setProperty($propertyName)
{
$this->$propertyName = new FieldProperty;
return $this->$propertyName;
}
}
Now you can do this:
$field->setProperty('textfield')->setLabel('my label')->setValue('some value');
If you want to maintain a reference between $this->$property->label and $this->label, assign the value by reference
$this->$property->label =& $this->label;
Demo ~ https://eval.in/955943
Be warned, the reference works both ways so if you assign a new label to $field->textfield->label, it will also change $field->label.
I feel a better approach would be to use magic methods to automatically create each property. Then you can keep track of them all in a private field and update their labels accordingly.
class Field
{
public $label;
private $properties = []; // this will store all your dynamic properties
public function __get($name) {
// lazily create properties on demand
if (!isset($this->properties[$name])) {
$this->properties[$name] = new \stdclass();
}
return $this->properties[$name];
}
public function setValue($property, $value)
{
// this will automatically create "$property" if it's not defined
$this->$property->value = $value;
$this->$property->label = $this->label;
return $this;
}
public function setLabel($label)
{
// Set the field label and any known property labels
$this->label = $label;
foreach($this->properties as $property) {
$property->label = $label;
}
return $label; // ¯\_(ツ)_/¯
}
}
If you want to do it OOP way, I guess, I need to do it like the following. Create the base Field class:
class Field
{
public $value;
public $label;
public function setValue($value)
{
$this->value = $value;
return $this;
}
public function setLabel($label)
{
$this->label = $label;
return $this;
}
}
Maybe, even make it abstract.
Then you can extend this class, creating specific fields:
final class TextField extends Field
{
}
$textField = (new TextField)
->setValue('something to print')
->setLabel('Label here');
Here is the demo.
This way, you can utilize polymorphism later in your program. Say you have an object, to which you pass a bunch of different objects of type Field (read extend Field). Then doing your way, this object wouldn't know what $property each of them has, and it wouldn't be able to access value and label - the objects don't share the common interface. But, with objects, that share the common interface (in our case extend the same base class), we can simply loop over the bunch of fields and retrieve values and labels.
In order to render this fields, the one (read a little bit ugly) solution would be to utilize get_class function:
switch ($class = get_class($field)) {
case 'TextField':
$this->renderTextField($field);
break;
case 'InputField':
$this->renderInputField($field);
break;
// ...
default:
throw new Exception("Cannot render $class.");
}
You are changing label property of $property ('textfield') not $this. It means var_dump($field->textfield->label); doesn't contain anything but var_dump($field->label); does.
Saying that you need to store $property's name so that later you can refer to it. A quick working solution would be:
<?php
class Field
{
public $label;
public $propName;
public function setValue($property, $value)
{
$this->propName = $property;
$this->$property = new \stdClass();
$this->$property->value = $value;
$this->$property->label = $this->label;
return $this;
}
public function setLabel($label)
{
$this->{$this->propName}->label = $label;
}
}
$field = new Field;
$field->setValue('textfield', 'something to print')->setLabel('Label here');
var_dump($field->textfield->value, $field->textfield->label);
Live demo
Sorry to necromance but I wanted to do the same thing but without creating methods for each, while staying nice and short (i didn't want ->setMethod('propertyName', $value))
class Field
{
public function __call($property, $args): self
{
$this->{$property} = $args[0];
return $this;
}
}
Sample use:
$field = (new Field)->label('label!')->value('value!');
And the value of $field:
Field Object
(
[label] => label!
[value] => value!
)
I also whitelisted what property names I'll allow to be set, and just ignore anything else.
In your code "label" has the value into Field Object and not into textfield object as you can see:
Field object {
label => (string) Label here
textfield => stdClass object {
value => (string) something to print
label => null
}
}
You colud use $field->label, because they are stored in different object, or try this:
class Field {
public function setValue($value) {
$this->property = new \stdClass();
$this->property->value = $value;
return $this;
}
public function setLabel($label) {
$this->property->label = $label;
return $this->property;
}
}
$field = new Field();
$field->setValue('something to print')->setLabel('Label here');
print $field->property->value;
print $field->property->label;
Output
something to printLabel here