What is the best way of creating and populating an Object from values passed in from a form?
If for example, i have a class Car with properties Colour, Model, Make, Year and a method Save, which will either insert or update the record.
I then have a form that has fields for all these values and it is submitted. I want to create an instance of class Car with the posted values and then call the Save method. All good and well.
But what is the best way of assigning the posted values to the objects internal properties. Assuming this is a simplified scenario and the actual situation would have many more properties, making individual Set calls long-winded.
Is it best to simply call the Set method for each one? Or pass in an array to a method (or the constructor) which then calls the Set methods? Or some other way?
Any advice on best practices is appreciated
Cheers
Stuart
If the properties are public:
foreach($_POST as $key => $value)
$obj->$key = $value;
If they require setters:
foreach($_POST as $key => $value) {
$set = 'Set' . $key;
$obj->$set($value);
}
You can use a bit of Reflection magic:
public function loadFromArray($array) {
$class = new ReflectionClass(get_class($this));
$props = $class->getProperties();
foreach($props as $p) {
if (isset($array[$p->getName()])
$p->setValue($this, $array[$p->getName]);
}
}
You can implement this method in a base class and make all yout object inherit from that, so you have this functionality in every object without repeating yourself in any class.
I would implement the magic function __set_state(), as it is intended for exactly this use case. There are multiple benefits of putting the implementation into that method:
It is very well defined, what this function does (It is defined in the PHP docs, in contrast to your own source code)
You can access private members within the function
Objects dumped with var_export() will be automatically reconstructed using this function.
EDIT As requested in the comments:
class A {
private $member = 1000;
public static function test(A $a) {
echo $a->member;
}
}
echo A::test(new A()); // outputs 1000
EDIT Fulfilling another request from the comments:
You cannot know on which class a static method was called unless you are using php 5.3, in which the required feature (late static binding) was introduced. What you can do in emulating get_called_class() by analyzing the current stack (using debug_backtrace()). If you have a working get_called_class() function, you can do the following:
class BaseClass {
public static function __set_state($state) {
$class = get_called_class();
// Assumption: Constructor can be invoked without parameters!
$object = new $class();
foreach (get_class_vars($class) as $member) {
if (!isset($state[$member])) {
// Member was not provided, you can choose to ignore this case
throw new Exception("$class::$member was not provided");
} else {
// Set member directly
// Assumption: members are all public or protected
$object->$member = $state[$member];
// Or use the setter-approach given by Chaos.
$setter = 'set' . ucfirst($member);
$object->setter($state[$member]);
}
}
return $object;
}
}
Well, you can convert the post array to an object in one step...
$mypostobject = (object) $_POST;
echo $mypostobject->Colour;
EDIT: added link to PHP docs. http://uk.php.net/manual/en/language.types.object.php
If you want to do this with your own class of object, then some kind of constructor or function that takes the post array and set up your class would be in order.
class MyCar {
public function __construct($postdata)
{
$this->Colour = $postdata['Colour'];
// etc
}
};
$car = new MyCar($_POST);
In case the object that you're posting may become more complex than a flat list of properties in the future (think nesting, arrays as values etc.), you could first create a JavaScript object on the client side and post it as JSON inside a single parameter. Then you can simply deserialize the JSON string into a PHP object. PHP 5+ has built-in support for JSON deserialization. Using JSON would allow you to be flexible in how complex your object can be.
create a constructor which will take all the attributes
class Car {
// private members …
public function __construct($color, $type, $model, $make) {
// assign your values here
}
// other methods
};
if you are dealing with associative arrays, you can also use a constructor with an array as argument:
public function __construct($car_properties) {}
and then loop your properties with
foreach($car_properties as $property => $value) {
if(isset($value)) $this->property = $value
}
be sure to sanitize/validate your input beforehand! (can’t stress this often enough)
Related
My question is basically exactly the same as the title says.
I was playing with TypeScript for a while now and there's a simple way to define the structure of the Object which is defining properties inside the Interface. I know that PHP does not support the properties in Interfaces, but is there any way to somehow define the structure of the Object (without using some abstract class) I'm passing or at least the Array (which keys need to be presented inside).
What I mean exactly is:
// I already sanitized that this method returns the exact same structure every time
$data = $this->storage->get($some);
// here I'm passing the data I obtained to my Builder
Builder::createFromArray($data);
// or
Builder::create($data);
class Builder {
public static function createFromArray(\ArrayOfSomeType $array) {}
public static function create(\ObjectOfSomeTypeWithPropertiesSpecified $obj) {}
}
Hope I explained it well.
As you said, there is no concept in PHP, that does exactly what you want. There is an ArrayObject class, that would fit your demands of an object with a loose amount of members.
class Builder {
public function create(iterable $data) : \ArrayObject {
$object = (new \ArrayObject())->setFlags(\ArrayObject::ARRAY_AS_PROPS);
foreach ($data as $key => $value) {
$object->offsetSet($key, $value);
}
return $object;
}
}
}
Example 1: Populate object with array
$object = Builder::create([ 'bla' => 'blubb', 'yadda' => 'blubb' ]);
var_dump($object->bla);
The builder returns an object with exact the same properties as the array had keys. All the properties contain the values the array has. You can iterate over the new object with foreach or an Iterator object.
Example 2: Populate object with another object
$class = new \stdClass();
$class->propertyA = 'B';
$class->propertyB = 'B';
$object = Builder::Create($class);
var_dump($object->propertyA);
With PHP nearly all objects are iterable. That means you can iterate over the properties of one object and pass them to our ArrayObject instance.
Common approach of hydration with PHP
There is another approach in PHP which is called hydration. It is a bit more complex than the shown example but pretty handy, if you got it.
// your entity / data model
class Car implements EntityInterface {
protected $horsepower;
public function getHorsepower() : int
{
if ($this->horsepower === null) {
$this->horsepower = 0;
}
return $this->horsepower;
}
public function setHorsepower(int $horsepower) : self
{
$this->horsepower = $horsepower;
return $this;
}
}
This is our data model (entity) of the type car. Our Car inherits from an interface. This is just for type hinting reasons in the hydrator. Our car has one property called horsepower. Of course a car can have more properties. But this is just for example. ;)
class ClassMethodsHydrator
{
public function hydrate(array $data, EntityInterface $entity) : EntityInterface {
foreach ($data as $key => $value) {
$methodName = 'set' . ucwords(strtolower($key));
if (method_exists($entity, $methodName) {
$entity->{$methodName}($value);
}
}
return $entity;
}
}
This is our small hydrator example. This class does, what your builder does. But in a more specific way. It takes an entity and hydrates it with the given data, if it matches a method of the entity.
$entity = (new Hydrator())->hydrate(
[
'horsepower' => 172,
'notexistingproperty' => 'bla',
],
new Car()
);
var_dump($entity->getHorsepower()); // 172
The advantage of hydration is simple. You only have models with known properties. That 's pretty safe and you know at any time, what your model can do for you. It would be senseless, that a car has a rudder for example. In this case a car only got what a car got. Like in the example the car only takes the horsepower member of the array.
I think you could force properites by creating getters and setter in interface. And then instead of
$item->something;
you could use
$item->getSomething();
I've always passed data manually between different classes. So for example I had some data produced by one class:
$someData = $Object->someMethod();
$moreData = $Object2->anotherMethod($someData);
But it feels clunky to me and it results in messy code that gets complicated. Especially if there are multiple different kinds of data passed around multiple classes.
So instead of doing that I've decided I will create a class DataContainer that groups every variable related to the process and then I will just pass this object around different classes. As it passes the processing pipeline, it will gather more and more data until almost every of its field is set to some value.
So for example I have a pipeline of processing data that gets modified by 4 different classes - instead of passing the data by value I will pass it by reference:
$myObject = $class1->method1(); // this class returns the DataContainer object
$class2->method2($myObject);
$class3->method3($myObject);
$class4->method4($myObject);
Is it considered a better choice? Or is there something better?
Keep in mind to make your code SOLID. (http://en.wikipedia.org/wiki/SOLID_(object-oriented_design))
In your case, you can create in the constructor of class2 a reference.
For example:
<?php
class Class1
{
private $class2;
public __construct(Class2 $class2)
{
$this->class2 = $class2;
}
public function CallMethodOfClass2()
{
$value = $this->class2->GetMethod();
$propertyValue = $this->class2->public_property;
}
}
?>
Or when your Class2 cant exists without Class1, make in the constructor of Class1 a new instance of Class2 like this:
<?php
class Class1
{
private $class2;
public __construct()
{
$this->class2 = new Class2();
}
public function CallMethodOfClass2()
{
$value = $this->class2->GetMethod();
$propertyValue = $this->class2->public_property;
}
}
?>
This is a pretty specific problem, which I can't find an answer for anywhere.
This applies to lots of languages but I'm particularly wanting to figure it out in PHP.
For the sake of this I'll be using 'events' as our classes. Keeping it simple there are 2 types:
abstract class Event{
protected $cost;
protected $startTime;
public function __construct(){
foreach($eventData as $key => $val){
if(property_exists($this, $key)){
$this->$key = $val;
}
}
}
}
and
class Party extends Event{
private $hasPointyHats;
public function __construct($eventData){
parent::__construct($eventData);
$this->hasPointyHats = $eventData->hasPointyHats;
}
}
Class Event has 2 props: cost and startTime. When it is constructed, it should be passed an associative array containing all of the parameters of the event, and it'll automatically set the property to the passed array value.
Class Party extends Event by establishing whether or not this event will have those awesome pointy party hats (I'm not going if it doesn't).
And no. No it doesn't. Because when you pass this object:
//Some result set...
$mysqli_result = $result->fetch_assoc();
which has cost, startTime, and hasPointyHats values (possibly even more!), you get the following little error:
Fatal error: Cannot access private property Party::$hasPointyHats in C:\somePath\Event.php on line 35
I understand why. Because in "Event", $this refers to the "party" object but it's private.
I am not looking to overwrite every property in the object in the super constructor, just the ones that belong to the super class (abstract class Event) itself. Is there a way to target the specific properties of the Event class and NOT the ones of the child class? That way, no matter what object I extend it with, I wont end up accidentally setting properties on the child class because the passed object had some conflicting property?
I assume it's some silly easy thing like super->property or whatever, but I need to still be able to use property_exists with it.
Thanks for help in advance.
You can use property_exists with just a class name instead of an object.
Like this:
abstract class Event{
protected $cost;
protected $startTime;
public function __construct($eventData){
foreach($eventData as $key => $val){
if(property_exists(__CLASS__, $key)){
$this->$key = $val;
}
}
}
}
property_exists() specifically states in the documentation that as of PHP 5.3, it checks the existence of a property independent of accessibility, which explains the behaviour you're seeing.
get_object_vars(), on the other hand, gets a list of the defined properties in scope, so should work better for your purposes.
Try something like this:
public function __construct() {
$props = get_object_vars($this);
foreach ($eventData as $key => $val) {
if (array_key_exists($key, $props)) {
$this->$key = $val;
}
}
}
This doesn't specifically check that it's a property of the parent class, just that it's accessible from the parent class.
If you really want to restrict to just the parent class, get_class_vars() might be even better:
public function __construct() {
$props = get_class_vars(__CLASS__);
foreach ($eventData as $key => $val) {
if (array_key_exists($key, $props)) {
$this->$key = $val;
}
}
}
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();
Well,
I have a problem (ok, no real problem, but I wanna try out something new) with creating objects. Actually I have some orders, which contains a list of orderitems.
These orderitems are used and so spreaded in the whole application, and I need a way to create them. The main problem is, I want to be able to create these objects in many different ways.
Actually I do this in the class constructor and check if the argument which is given.
(I'm using php, so there is no overloading support from the language as you surely know :))
A simple and quick Example
class foo {
protected $_data=null;
public function __contruct($bar){
if (is_array($bar)){
$this->_data=$bar;
}
else {
$dataFromDb=getDataFromDatabase
$this->_data=$dataFromDb;
}
}
}
Anyway, if I want to create my object by giving another type of parameter, lets say a xml-document encapsulated in a string I need to put all this stuff in my constructor.
If the process for creating an object is more complicated, I eventually need to create a seperate method for each type, I want to initiate. But this method is only called when this special type is created. (I think you got the problem :))
Another problem comes to mind, if I need more parameters in the constructor to create a concrete object, I have modify all my code, cause the contructor changed. (Ok, I can give him more and more parameters and work with default values, but that is not what I really want).
So my Question is, which pattern fits this problem to solve my creation of a concrete object. I thought about creating a factory for each way I want to create the concrete object. But I'm not sure if this is a common solution to solve such a problem.
IF its only the signature of the constructor changing i would do it like so (a la the Zend Framework universal constructor):
class foo {
// params
public function __construct($options = null)
{
if(null !== $options)
{
$this->setOptions($options);
}
}
public function setOptions(array $options){
foreach ($options as $name => $value){
$method = 'set' . $name;
if(method_exists($this, $method)
{
$this->$method($value);
}
}
return $this;
}
}
And this essntially means all your constructor parameters are array elements with named keys, and anything you want used in this array during initialization you create a setter for and then its automatically called. The down side is the lack of effective hinting in IDEs.
On the otherhand if you want to have specific constructors then i might go with a factory but still use much the same approach:
class foo {
public static function create($class, $options)
{
if(class_exists($class))
{
$obj = new $class($options);
}
}
}
Of course you could alternatively use PHP's reflection to determine how to call the constructor instead of just injecting an arbitrary array argument.
you could simply make it a factory with optional params :)
class Example_Factory
{
public static function factory($mandatoryParam, $optionalParam = null)
{
$instance = new self;
$instance->setMandatory($mandatoryParam);
if ($optionalParam !== null) {
$instance->setOptional($optionalParam);
}
return $instance;
}
public function setMandatory($in)
{
// do something....
}
public function setOptional($in)
{
// do some more...
}
}