I'm using symfony and doctrine.
The server gets a HTTP PATCH request for the URL /company/{id} containing the property of a model and its value like {"name": "My new name"} The new value needs to be persisted into the DB.
$request = Request::createFromGlobals();
$requestContentJSON = $request->getContent();
$requestContentObj = json_decode($requestContentJSON);
$repository = $this->getDoctrine()->getRepository('MyBundle:Company');
$company = $repository->find($id);
Now I could just enter $company->setName($requestContentObj[0]); but the property being received will vary. Right now I'm using the following code to be able to handle every property:
foreach($requestContentObj as $key => $value){
switch($key){
case 'name':
$company->setName($value);
break;
case 'department':
$company->setDepartment($value);
break;
case 'origin':
$company->setOrigin($value);
break;
case 'headquarters':
$company->setHeadquarters($value);
break;
case 'email':
$company->setEmail($value);
break;
case 'twitterid':
$company->setTwitterId($value);
break;
case 'description':
$company->setDescription($value);
break;
}
}
But this doesn't look very smart especially because I know that I will have other entities like news, products, users, etc that will have their properties updated in the same manner. I'd like to do something like this:
$company->set("property", "value");
First thought that crossed my mind was to put this switch statement inside the company class inside this set function and also inside all the other entity classes I have. But is there a better way? Maybe symfony/doctrine has the solution already built-in, but I didn't find anything that would suit me.
I still want to use setters and getters as a long-term investment.
Thank you.
Assuming you'll have the property names similar to method names.
You can do something like this. To set multiple properties.
Class customer {
protected $_email;
public function __construct(array $config = array()){
$this->setOptions($config);
}
public function getEmail(){
return $this->_email;
}
public function setEmail($email){
$this->_email = $email;
}
public function setOptions(array $options)
{
$_classMethods = get_class_methods($this);
foreach ($options as $key => $value) {
$method = 'set' . ucfirst($key);
if (in_array($method, $_classMethods)) {
$this->$method($value);
} else {
throw new Exception('Invalid method name');
}
}
return $this;
}
public function setOption($key, $value){
return $this->setOptions(array($key, $value));
}
}
Now you can simply do this:
$array = array('email' => 'abc.#gmail.com');
$customer = new Customer($array);
echo $customer->getEmail();
My inital thought would be to add a merge method to your class, like so:
<?php
// example Company entity
class Company
{
private $name;
function setName($name)
{
$this->name = $name;
}
function getName()
{
return $this->name;
}
function merge(\stdClass $obj)
{
// get the object vars of the passed object
// iterate, and replace matching properties
foreach (get_object_vars($obj) as $prop => $val) {
if (property_exists($this, $prop)) {
$this->$prop = $val;
}
}
}
}
$company = new Company();
// mocking your request object
$requestContentObj = new stdClass();
$requestContentObj->name = 'acme';
$company->merge($requestContentObj);
var_dump($company);
Yields:
class Company#1 (1) {
private $name =>
string(4) "acme"
}
This silently dumps any passed values that do not match any properties in your Company class, which may or may not be what you want. Hope this helps :)
What I can propose is not using the setters, but it seems a good fit for your problem.
In doctrine 1.2.4, you could use DQL as follow:
$q = Doctrine_Core::getTable("myTable")->createQuery("q")
->update()
->where("id = ?", $id);
foreach($requestContentObj as $key => $value)
{
$q->set($key, "?", $value);
}
$q->execute();
Related
I recently studied magic methods, __get and __set, and was wondering how to actually set and get multiple properties in the class.
I know it works perfectly with only one variable or array, but I'm not sure about accessing multiple variables.
Is there anyone who could explain this to me?
class myMagic2 {
public $data;
public $name;
public $age;
public function __set($item, $value) {
$this->item = $value;
}
public function __get($item){
return $this->item;
}
}
Is there a way to access all variables ($data, $name, $age)?
When i work at projects i always have these methods:
public function __set($name, $value)
{
//see if there exists a extra setter method: setName()
$method = 'set' . ucfirst($name);
if(!method_exists($this, $method))
{
//if there is no setter, receive all public/protected vars and set the correct one if found
$vars = $this->vars;
if(array_search("_" . $name, $vars) !== FALSE)
$this->{"_" . $name} = $value;
} else
$this->$method($value); //call the setter with the value
}
public function __get($name)
{
//see if there is an extra getter method: getName()
$method = 'get' . ucfirst($name);
if(!method_exists($this, $method))
{
//if there is no getter, receive all public/protected vars and return the correct one if found
$vars = $this->vars;
if(array_search("_" . $name, $vars) !== FALSE)
return $this->{"_" . $name};
} else
return $this->$method(); //call the getter
return null;
}
public function getVars()
{
if(!$this->_vars)
{
$reflect = new ReflectionClass($this);
$this->_vars = array();
foreach($reflect->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED) as $var)
{
$this->_vars[] = $var->name;
}
}
return $this->_vars;
}
So with them i give myself the freedom to create extra setter/getter for properties if i want to manipulate them before writing/returning. If no setter/getter exists for the property it falls back to the property itself. With the method getVars() you receive all public and protected properties from the class.
My class properties are always defined with an underscorce so you should probably change that.
You could follow this pattern.
Note: in the example in the post the magic methods would not have been called for $obj->data, $obj->name, or $obj->age because these values where already accessible as a public property. I changed them to be protected and altered the name to hide them.
<?php
class myMagic2{
protected $_data;
protected $_name;
protected $_age;
public function __set($item, $value){
switch($item){
case "data":
$this->_data = $value;
break;
case "name":
$this->_name = $value;
break;
case "age":
$this->_age = $value;
break;
default:
throw new Exception("Property $name does not exist.");
}
}
public function __get($item){
switch($item){
case "data":
return $this->_data;
break;
case "name":
return $this->_name;
break;
case "age":
return $this->_age;
break;
default:
throw new Exception("Property $name does not exist.");
break;
}
}
}
Normally you would do a little more then use the magic methods as a proxy to set and get classes properties. You would so validation or filtering or in some other way augmenting the operation. If you are just going to get or set a property you might as well just make the properties public and forgo using the magic methods.
We have a system where we have reason to instantiate an object before we know what its specific type is. For example, we wish to instantiate a class "media" before we know whether the final class will be "book" or "cd".
Here is what we are doing. We instantiate the "media" object, and once we know what type of media it is, we instantiate a "book", passing in the media object.
class Book extends Media
{
public function __construct( $parent )
{
$vars = get_object_vars( $parent );
foreach( $vars as $key => $value )
$this->$key = $value;
}
}
//elsewhere
$item = new Media();
$item->setPrice( $price );
//other code, figure out the item type
$item = new Book( $item );
Is there a better way to do something like this? Is this dynamic polymorphism?
In case u really can't determine what type of object is i can recommend u factory pattern. With this pattern u has only one entry point and this helps u to get yr code simpler and readable.
Short example:
class ObjectFactory {
public static function factory($object, $objectType)
{
$result = false;
switch ($objectType) {
case 'book':
$result = new Book;
$result->setParams($object->getParams());
break;
case 'otherType':
$result = new OtherType;
$result->setParams($object->getParams());
// or
$result->setParamsFromObject($object);
break;
... //etc
}
return $result;
}
}
class Book extends MediaAbstract
{
public function __set($name, $value)
{
$this->_params($name, $value);
}
public function __get($name)
{
return $this->_params[$value];
}
public function setParams(array $params)
{
$this->_params = $params;
return $this;
}
public function getParams()
{
return $this->_params;
}
// in case u want store params as properties
public function setParamsFromObject($object)
{
$vars = get_object_vars($object);
foreach ($vars as $key => $value) {
$this->$key = $value;
}
return $this;
}
}
$media = new Media;
$media->setParams($params)
// some stuff
//...
$book = ObjectFactory::factory($media, $objectType);
Here is the way I do:
$aNewObject = new MyObj();
$aNewObject->set_id($row->id);
$aNewObject->set_user_id($row->user_id);
$aNewObject->set_title($row->title);
$aNewObject->set_url($row->url);
$aNewObject->set_description($row->description);
$aNewObject->set_status($row->status);
as you can see, I follow a name convention, which the object and the data base field is 100% match, I think there should have a way to help me to do it lazier, any recommendation?
You can get even lazier by only writing
$aNewObject = new MyObj($row);
and having a constructor that sets the object's properties based on the contents of $row
You could do the setting dynamically by iterating over the fields (if that is meant by lazier):
$fields = array('user_id', ...);
foreach($fields as $field)
{
$setter = "set_{$field}";
$aNewObject->$setter($row->{$field});
}
It depends then where you want to place that code. Either just inline, as part of a function of MyObj (importRow($row)) or in a global helper function that always calls all setters matching object properties.
Within the class constructor:
$aNewObject = new MyObj($row);
class MyObj
{
public function __construct($row = null)
{
...
$this->importRow($row);
}
public function importRow($row = null)
{
if (null === $row)
return;
foreach($row as $field => $value)
{
$setter = "set_{$field}";
$this->$setter($value);
}
}
...
}
To prevent duplicate code across different classes (missing traits support in PHP < 5.4), a global static function or object can do it:
$aNewObject = new MyObj();
new Setter($aNewObject, $row);
# or
$aNewObject = Setter::fill('MyObj', $row);
class Setter
{
private $object;
public function __construct($class, $data)
{
// works on classnames or objects
if (is_string($class))
$object = new $class();
else
$object = $class;
$this->object = $this->import($object, $data);
}
private function import($object, $data)
{
foreach($data as $field => $value)
{
$setter = "set_{$field}";
$object->$setter($value);
}
return $object;
}
public function getObject()
{
return $this->object;
}
public static function fill($class, $data)
{
$self = new __CLASS__($class, $data);
return $self->getObject();
}
}
Lazier, it is not recommended, because it will make it hard to maintain by other programmers.
If you still want to do that, you should do with PHP reflection: http://www.php.net/manual/en/intro.reflection.php
or as #hakre answered.
i use
$myblogrepo = $this->_doctrine->getRepository('Entities\Blog')->findBy(array('id' => 12);
i access via
foreach($myblogrepo as $key =>$value){
echo $key . $value;
}
how can i get the field names? i thought the key => would work but it print s the key as 0
so i thought this would work:
foreach($myblogrepo[0] as $key =>$value){
echo $key . $value;
}
but still nothing..
}
In all likelihood, your Blog entity's properties are declared as protected. This is why you can't iterate over them from outside the the Entity itself.
If you're using your Blog entities in a read-only fashion, and only need access to the properties marked as #Columns (read: you don't need to call any methods on your entity), you might consider using array-hydration. That way you'll be dealing with simple arrays, and $k=>$v type iteration will work fine.
Otherwise, you'll need to create some kind of getValues() method on your entity class. This could be a simple implementation that just builds and array and returns it.
Finally, you could create a general-purpose getValues() as a utility function that uses doctrine's class metadata to figure out what columns and entity has, and operate on those data. A simple implementation like this:
function getEntityColumnValues($entity,$em){
$cols = $em->getClassMetadata(get_class($entity))->getColumnNames();
$values = array();
foreach($cols as $col){
$getter = 'get'.ucfirst($col);
$values[$col] = $entity->$getter();
}
return $values;
}
EDIT - A more mature version of the above method seems to be available here - I've not played with it yet, but it looks promising.
If you just need to get the properties of the entity in a fast and easy way, this is what I do in my projects:
All my entities inherit from an EntityBase class, which has the following method:
public function toValueObject()
{
$result = new \stdClass();
foreach ($this as $property => $value) {
$getter = 'get' . ucfirst($property);
if (method_exists($this, $getter)) {
$result->$property = $this->$getter();
}
}
return $result;
}
So all I have to do is call $entity->toValueObject() and I obtain a standard object with all of the entity's properties as public properties.
Use findOneBy instead of findBy to select a single row.
$myblogrepo = $this->_doctrine->getRepository('Entities\Blog')->findOneBy(array('id' => 12);
Your key was 0 because it was the first row in a possible multi-row result.
This is a my implementation of a serializer class that also check if it is a doctrine entity:
/**
* JsonApiSerializer constructor.
* #param EntityManagerInterface $em
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* #param $payLoad
* #return string
*/
public function serialize($payLoad, $type)
{
$serializedPayload = new \stdClass();
$serializedPayload->data = new \stdClass();
$serializedPayload->data->type = $type;
if ($this->isDoctrineEntity($payLoad)) {
$this->addEntityColumnValues($serializedPayload, $payLoad);
}
return json_encode($serializedPayload);
}
private function isDoctrineEntity($payLoad)
{
if (is_object($payLoad)) {
$payLoad = ($payLoad instanceof Proxy)
? get_parent_class($payLoad)
: get_class($payLoad);
}
return !$this->em->getMetadataFactory()->isTransient($payLoad);
}
private function addEntityColumnValues(&$serializedPayload, $entity){
$serializedPayload->data->attributes = new \stdClass();
$classMetaData = $this->em->getClassMetadata(get_class($entity));
$columnNames = $classMetaData->getColumnNames();
foreach($columnNames as $columnName){
$fieldName = $classMetaData->getFieldForColumn($columnName);
$getter = 'get'.ucfirst($fieldName);
$serializedPayload->data->attributes->$columnName = $entity->$getter();
}
}
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