Ive made a class that stores columns and rows these 2 are both arrays who both have extra data, like this:
<?php
class Table{
private array $Columns = array();
private array $Rows = array();
public function setColumn(Columns $c)
{
array_push($this->Columns, $c);
}
public function getColumn(): array
{
return $this->Columns;
}
public function setRow(Rows $c)
{
array_push($this->Rows, $c);
}
public function getRow(): array
{
return $this->Rows;
}
}
class Columns{
public string $Someinfo;
public function setSomeinfo(string $c)
{
$this->Someinfo= $c;
}
public function getSomeinfo(): int
{
return $this->Someinfo;
}
}
class Rows{
public string $Someinfo;
public function setSomeinfo(string $c)
{
$this->Someinfo= $c;
}
public function getSomeinfo(): int
{
return $this->Someinfo;
}
}
?>
My Symfony serializer sees and uses this class when its public but when I turn it private it suddenly stops working.
Symfony serializer:
$serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
return $serializer->deserialize($data, $table, 'json');
Ive tried almost all types of set, get and adders but none works or gets triggered by the serializer. The documentation has some examples but they are the same as my code at chapter: Deserializing a object.
https://symfony.com/doc/current/components/serializer.html#component-serializer-attributes-groups
Related
so a class:
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
its beign created and updated:
$obj = new ToBeUsed();
$obj->setSomething('a');
and passed to another object
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //// !!!!! THIS IS BAD!
}
}
now a classic DI example, except that the passed object should be "dulled" - only some methods are allowed to use. E.g. getSomething() is allowed to use, but setSomething() is not. What pattern / practice can get away with it? There used to be friend classes is C but its Php...
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$dbg = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
if(count($dbg) > 1){
return;
}
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
echo $this->obj->getSomething().PHP_EOL; // a
$this->obj->setSomething('b'); // this does nothing
echo $this->obj->getSomething().PHP_EOL; // a
}
}
$obj = new ToBeUsed();
$obj->setSomething('a');
$obj2 = new UseIt($obj);
$obj2->work();
Alternatively, you can perform more complex checks on debug_backtrace() output.
I would probably do something with Interfaces, it doesn't prevent a method form being used. But "they" (whoever they is) would be using it outside of the Interface for $obj.
Like this:
class ToBeUsed implements ToBeUsedInterface
{
private $a;
public function getSomething()
{
return $this->a;
}
public function setSomething($a)
{
$this->a = $a;
}
}
interface ToBeUsedInterface{
public function getSomething();
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsedInterface $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //This now exists outside of the interface for $obj
}
}
In terms of IDE's this would prevent the methods from auto-completing as well.
The only other thing I can think of, ( besides the other answer ) would be to set the method to protected and then use ReflectionMethod to change the viability, when you want to use it.
Another Option, is Using Reflection
class ToBeUsed
{
private $a;
public function getSomething()
{
return $this->a;
}
protected function setSomething($a)
{
$this->a = $a;
}
}
$ToBeUsed = new ToBeUsed();
$ReflectionMethod = new ReflectionMethod($ToBeUsed, 'setSomething');
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invoke($ToBeUsed, 'foo');
echo $ToBeUsed->getSomething();
Outputs:
foo
You can see it live here
And Obviously sense it's protected under normal conditions, it could not be used inside UseIt. If I was going to use this for any amount of code, I would extend or wrap the Reflection class. Just to make the call a bit more concise, like this:
class MyReflector
{
public static function invoke($class, $method, ...$args)
{
$ReflectionMethod = new ReflectionMethod($class, $method);
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invokeArgs($class, $args);
}
}
$ToBeUsed = new ToBeUsed();
MyReflector::invoke($ToBeUsed,'setSomething', 'foo');
Please note I got all fancy with the variadic ...$arg which is for PHP 5.6+ it just lets you do
MyReflector::invoke($ToBeUsed,'setSomething', 'foo', 'bar');
And $args would be ['foo','bar'] in the first example it's just ['foo'] which can be used for invokeArgs for the second argument which takes an array of arguments to pass on to the actual method.
Ok so currently have this function in controller, which is called multiple times.
public function formatFloat($value)
{
return (float)sprintf('%0.6f', $value);
}
So I am trying to use getters and setters so I can just use
$model->$whatever;
and the formatting will be done.
In my model I have
public function getChargePeak()
{
return $this->charge_peak;
}
public function setChargePeak($value)
{
return $this->charge_peak = (float)sprintf('%0.6f', $value);
}
but when doing
$peak = $model->chargepeak;
var_dump($peak);die;
it is still returning as a string
If the charge_peak property is stored as string and you need a float in you app you should use
public function getChargePeak()
{
return floatval($this->charge_peak);
}
Anyway you should store the values in a coherent way as you use the values in your app ..
http://php.net/manual/en/function.floatval.php
So I suggest u another pattern: decorator and helpers. You should use a controller only to get data from request, prepare it for model and send it to view.
Formatting values is a helper logic. So create a new class
\common\helpers\Number.php
namespace common\helpers;
class Number
{
public static function formatFloat($value)
{
return (float)sprintf('%0.6f', $value);
}
}
Then create decorator for your model:
namespace common\models\decorators;
class YourModelDecorator
{
/**
* YourModel
*/
private $model;
public function __construct(YourModel $model)
{
$this->model = $model;
}
public function __get($name)
{
$methodName = 'get' . $name;
if (method_exists(self::class, $methodName)) {
return $this->$methodName();
} else {
return $this->model->{$name};
}
}
public function __call($name, $arguments)
{
return $this->model->$name($arguments);
}
public function getChargePeak()
{
return \common\helpers\Number::formatFloat($this->model->charge_peak);
}
}
and send it to view for example:
public function actionView($id)
{
$model = $this->loadModel($id);
$this->render('view', [
'model' => new \common\models\decorators\YourModelDecorator($model)
]);
}
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.
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 wrote an array wrapper class PersonArray which can contain objects of a certain type (Person). Every person has a unique getHash() function which returns the ID + Name as a unique identifier. This allows for speedy retrieval of the Person from the PersonArray. The PersonArray actually holds two internal Arrays. One for the storage of Person objects ($items), and one for the storage of the Hash values ($itemsHash).
I want to create a insertAt(index, Person) function which puts the Person object at the [index] position in the $items array. Is there a way to insertAt a certain position in an array? If so how can I also update the $itemsHash of the PersonArray?
class Person {
function getHash() {
return $this->id . $this->name;
}
}
class PersonArray implements Iterator {
public $items = array();
public $itemsHash = array();
public function Find($pKey) {
if($this->ContainsKey($pKey)) {
return $this->Item($this->internalRegisteredHashList[$pKey]);
}
}
public function Add($object) {
if($object->getHash()) {
$this->internalRegisteredHashList[$object->getHash()] = $this->Count();
array_push($this->items, $object);
}
}
public function getItems() {
return $this->items;
}
function ContainsKey($pKey) {}
function Count() {}
function Item($pKey) {}
//Iteration implementation
public function rewind() {}
public function current() {}
public function key() {}
public function next() {}
public function valid() {}
}
You may find it is faster and easier to use PHP's associative arrays rather than re-implementing them.
As an aside you can also implement the simpler IteratorAggregate if you are actually just iterating over an array.
e.g.
class PersonArray implements IteratorAggregate {
public $items = array();
public function getItems() {
return $this->items;
}
public function Add($object) {
if($object->getHash()) {
$this->items[$object->getHash()] = $object;
}
}
public function Find($pKey) {
if(isset($this->items[$pKey])) {
return $this->items[$pKey];
}
}
public function insertAt($index, $person) {
$tmp = array_slice($this->items, 0, $index);
$tmp[$person->getHash()] = $person;
$tmp = array_merge($tmp, array_slice($this->items, $index));
$this->items = $tmp;
}
//IteratorAggregate implementation
public function getIterator() {
return new ArrayIterator($this->items);
}
}