I've been suggested to use Mappers to separate object storage in db from real object behaviour, which sounds like a nice idea. But being used to other OOP languages like Java or SmallTalk, I always try to encapsulate my objects the most I can.
I've read several mappers examples and some of them access the variables of the domain object directly (which would be my last choice, though I know it's more comfortable).
The only solution I currently come up with is something like:
class User{
public __construct($mapped_data){
foreach($data as $key=>$value){
$this->$key=$value;
}
...
}
class UserMapper{
private populate($user,$db_data){
...
$map=array('id'=>'id','psw'=>'password','mail'=>'email', ...);
foreach($db_data as $key=>$value){
$mapped_data[$map[$key]]=$value;
}
return new User($mapped_data);
}
}
The map is because the class User doesn't have to know the db column names at all.
Is this approach correct? Is there a better one?
Thanks
I would suggest that Data Access Objects are a more appropriate pattern for what you want to achieve. The Mapper that you show above just increases the complexity but doesn't really provide much in the way of benefits.
In terms of getters and setters, you can use some of PHP's magic methods to clean up the code above. The following allows you to store your data in a protected array while providing access to it that looks like it's just via the object property, but is actually through a method (so that you can provide your own logic there; make things read only, or hide some, etc).
<?php
class User {
protected $_data = array();
public function __construct($data) {
$this->_data = $data;
}
// Get any value
public function __get($key) {
return array_key_exists($key, $this->_data) ? $this->_data[$key] : null;
}
// Set any value; leave out to make the data read only outside the class
public function __set($key, $val) {
$this->_data[$key] = $val;
}
// You should also implement __isset() and __unset()
}
You can then access the data like
<?php
$user = new User(array('foo' => 'bar'));
// Outputs "bar"
echo $user->foo;
You might also simplify this all for yourself by using an existing PHP ORM library.
Related
Often I have to create an object from a parsed csv or other file. These object have lots of fields. So I create a class with alot of variables and the generate the getters and setters.
Then I write a parsing function, something like:
public function parseCsvArrayToObject($csvArray){
foreach($csvArray as $csvLine){
$object = new $objectType();
$object->setFirst($csvLine[0]);
$object->setSecond($csvLine[1]);
$object->setThird($csvLine[2]);
....
}
}
Is there a way to just print out all setters for this class, it is a tedious job, if there isn't, that's fine, but if there is that would be genius.
I think, what you are asking for is not possible.
But you can build your classes in a way, that you don't need the setters and getters for each field individually.
You could just implement the magic __get() and __set(). With them you need to access the fields by name $object->first;
If you like the setters and getters you can implement the magic __call() and react according to the function name passed. This way you have $object->getFirst();
Rough example:
public function __call($name, $arguments)
{
if (substr($name, 0, 3) == 'get') {
$field = lcfirst(str_replace('get', '', $name));
if (isset($this->$field)) {
return $this->$field;
}
return null;
}
}
Maybe late, but may help people come along like me:
You can write a small script that generates the classes including the parsing methods you need.
I understand that multiple inheritance1 is simply not supported in PHP, and while many "hacks" or workarounds exist to emulate it, I also understand that an approach such as object composition is likely more flexible, stable, and understandable than such workarounds. Curiously, PHP's 5.4's traits will be the fitting solution, but we're not quite there yet, are we.
Now, this isn't simply an "amidoinitrite?" question, but I'd like to ensure that my approach makes sense to others.
Given I have classes Action and Event (there are more, but we'll keep it brief) and they both require (near) identical methods, the obvious approach would be; create a common base class, extend and go; they are, after all, conceptually similar enough to constitute being siblings in a class hierarchy (I think)
The problem is Event needs to extend a class (Exception) that itself cannot extend anything. The methods (and properties) all pertain to "attribute" values, we'll call them "options" and "data", where "options" are values stored at class level, and "data" are values stored at instance level.
With exception of (no pun intended) the Exception class, I can simply create a common class that all pertinent objects extend in order to inherit the necessary functionality, but I'm wondering what I can do to avoid the seemingly inevitable code duplication in Event; also, other classes that are not conceptually similar enough to be siblings need this functionality.
So far the answer seems to be, using the object composition approach, create a Data class, and manage it at two points:
At object instantiation, create a Data instance to be used with the object as "data".
At some point (through a static initialize() method perhaps) create a Data instance to be used statically with the class as "options".
Interfaces, named IData and IOption for example, would be implemented by classes needing this functionality. IData simply enforces the instance methods of the Data class on the consumer, and calls would be forwarded to the instance Data property object, whereas IOption would enforce similarly named methods (substitute "data" for "option") and those methods would forward to the static Data property object.
What I'm looking at is something like this (the methods are somewhat naive in appearance, but I've slimmed them for brevity here):
interface IData{
public function setData($name, $value);
public function putData($name, &$variable);
public function getData($name = null);
}
interface IOption{
public static function initializeOptions();
public static function setOption($name, $value);
public static function setOptions(Array $options);
public static function getOptions($name = null);
}
class Data implements IData{
private $_values = array();
public function setData($name, $value){
$this->_values[$name] = $value;
}
public function putData($name, &$variable){
$this->_values[$name] = &$variable;
}
public function getData($name = null){
if(null === $name){
return $this->_values;
}
if(isset($this->_values[$name])){
return $this->_values[$name];
}
return null;
}
}
class Test implements IData, IOption{
private static $_option;
private $_data;
public static function initializeOptions(){
self::$_option = new Data();
}
public static function setOption($name, $value){
self::$_option->setData($name, $value);
}
public static function setOptions(Array $options){
foreach($options as $name => $value){
self::$_option->setData($name, $value);
}
}
public static function getOptions($name = null){
return self::$_option->getOptions($name);
}
public function __construct(){
$this->_data = new Data();
}
public function setData($name, $value){
$this->_data->setData($name, $value);
return $this;
}
public function putData($name, &$variable){
$this->_data->putData($name, $variable);
return $this;
}
public function getData($name = null){
return $this->_data->getData($name);
}
}
So where do I go from here? I can't shake the feeling that I'm moving away from good design with this; I've introduced an irreversible dependency between the client classes and the storage classes, which the interfaces can't explicitly enforce.
Edit: Alternatively, I could keep the reference to Data (wherever necessary) public, eliminating the need for proxy methods, thus simplifying the composition. The problem then, is that I cannot deviate from the Data class functionality, say for instance if I need to make getData() act recursively, as this snippet exemplifies:
function getData($name = null){
if(null === $name){
// $parent_object would refer to $this->_parent
// in the Test class, given it had a hierarchal
// implementation
return array_replace($parent_object->getData(), $this->_values);
}
// ...
}
Of course, this all boils down to separate definitions on a per-class basis, to support any deviation from a default implementation.
I suppose the end-all here, is that I'm having trouble understanding where code duplication is "alright" (or more accurately, unavoidable) and where I can extract common functionality into a container, and how to reference and use the contained functionality across classes, deviating (typically negligibly) where necessary. Again, traits (in my cursory testing on beta) seem to be a perfect fit here, but the principle of composition has existed long before 5.4 (and PHP entirely for that matter) and I'm certain that there is a "classic" way to accomplish this.
1. Interestingly, the page for multiple inheritance at Wikipedia has been flagged for copyright investigation. Diamond problem seemed like a fitting substitute.
EDIT: I've just read your question again and you seem to be suggesting that you are actually using the getters and setters to manipulate the data. If this is the case then could you provide me with more detail on what it is that you're trying to achieve. I suspect that how you've decided to model your objects and data is what has led you to this situation and that an alternative would solve the problem.
You don't need multiple inheritance. You don't even need most of the code you've written.
If the purposes of classes 'Data' and 'Option' is to simply store data then use an array. Or, if you prefer the syntax of an object cast the array to an object or an instance of stdClass:
$person = (object)array(
'name' => 'Peter',
'gender' => 'Male'
);
OR
$person = new stdClass;
$person->name = 'Peter';
$person->gender = 'Male';
Having a whole bunch of getters and setters that don't actually do anything to the data are pointless.
Other similar questions:
Why I should use __get() and __set()-magic methods in php?
When do/should I use __construct(), __get(), __set(), and __call() in PHP?
First, I completely understand how to implement __get and __set methods in PHP and have come across scenarios where using these methods is beneficial.
However, I have encountered, and written, code that looks like the following:
class SomeObject {
protected $data = array();
public function __set($key, $val) {
$this->data[$key] = $val;
}
public function __get($key) {
return $this->data[$key];
}
}
Why do we need to do this? I have used objects with no __get or __set methods defined and can still add undefined properties, retrieve the values associated with those properties, invoke isset() and unset() on those properties.
Do we really need to implement these magic methods, using code like above, when objects in PHP already exhibit similar behavior?
Note, I'm not referring to when more code is used in the __set and __get methods for special handling of the data requested but very basic implementation like the code above.
If you are using them just as a glorified wrapper over an array (i.e. absolutely no extra logic), I don't believe they offer any benefit. They do offer a drawback however, in that you need to write the code for them. And that code may be buggy or incomplete.
Did you leave out __isset for brevity? Because if you do use __get and __set but do not provide an __isset, you 've just written yourself a bug.
See what I did there?
In the example you gave, it's very easy to overwrite $data with a new array. If you were dealing with properties, you would have to iterate over an array and perform $this->$key = $data; (to which you may have data leakage).
In more detail:
protected function setData($arr)
{
$this->data = $arr;
}
vs.
protected function setData($arr)
{
foreach($arr as $key => $data)
{
$this->$key => $data;
}
}
With the example above, keys which don't appear in $arr in the current call, but have in previous calls, will not be overwritten, which can lead to undesired results.
In the end, it just depends on what makes the most sense for you.
As a side note, in the example code you gave, you should be sure you're implementing __isset() when you have __get(). If you don't, you'll get unexpected results when using functions like isset() or empty().
To me the sample code looks like "We want all the downsides of an object combined with all the misuse potential of an array"
The only reason I can see for going for something like that is when you are starting to refactor from arrays into objects.
You replace all [''] access with -> but you don't break anything once it compiles again.
In the next step you can start adding rules for specific properties.
public function __set($key, $val) {
if($key == "money") { throw new Exception("Can't touch this"); }
$this->data[$key] = $val;
}
public function __get($key, $val) {
if($key == "money") { return $this->x * $this->y; }
$this->data[$key] = $val;
}
But in general I don't see any use going in that direction. Creating real "value objects" for OO data structures seems a lot more useful.
Doing something like that just for the sake of "I like -> better than [''] isn't a valid use case in my book."
For most use cases with "Objects as data structures" I'd much rather have:
class SomeObject {
protected $a, $b, $c;
public function __construct(TypeA $a, TypeB $b, TypeC $c) {
$this->a = $a;
$this->b = $b;
$this->c = $c;
}
/** #return TypeA */
public function getA($key) {
return $this->a;
}
// an so on
}
That documents the expected values. Works with native type hinting without the need of writing that your self and to me is what I'd expect to get as a "data structure".
If there is a good reason that a class should act like an array then ArrayAccess seems like a much better choice then that kind of magic to me.
There are quite a few different reasons you might want to use this, it all depends on how you want to control your class. A couple of examples:
1.) You have some attributes on your object that are related to a key -> value database. If more values are added, you would like to update the database with these. So in this method, you would have some sort of flag that indicates there is a $new_data array that needs to be synchronized with the database
2.) You want to strictly enforce that an object will only have the variables attached to it that you want to be there. If one doesn't exist for it, this can throw an exception.
3.) You've abstracted a database such that gets/sets will have an immediate impact on their corresponding values in it.
And many others.
In your example, no, you don't.
However, when we go into more complicated classes, you may want to define setters and getters. A sample __set would then be :
function __set($key, $val) {
if (method_exists($this, 'set'.ucfirst($key))) {
$this -> {'set'.ucfirst($key)}($val);
} else {
$this -> data[$key] = $val;
}
}
With this code you can simply define a function setTestKey which will set $this->data['testKey'] to the value in $value, and call it via $storage->testKey = 1;
I'm with you, I've never been a fan of magic methods because I feel that it breaks encapsulation. If you allow calling code to dictate object behavior that the object isn't aware of you risk un-expected behavior, which is harder to track down compared to having static setters/getters accessing defined member variables.
With that being said I have seen some clever instances where magic methods appear to be useful. One instance is calling a logger and defining the loglevel in which you want the message to appear.
Consider the following (I use this in my production code):
// Only works with php 5.3 or greater (uses __callstatic)
class MyCustom_Api_Log
{
/**
* Wrapper method for getting a symfony logger object
*
* #return object
*/
public static function getLogger()
{
return sfContext::getInstance()->getLogger();
}
/**
* Magic method for logging all supported log levels
*
* #param string
* #param array
*/
public static function __callStatic($level, $params)
{
// Only concerned with message at this time
// However any number of params can be passed in
$message = shift($params);
self::getLogger()->$level($message);
}
}
// All these calls are made to __callStatic, so no need for individual methods
MyCustom_Api_Log::err(array('Oh noes something went wrong'));
MyCustom_Api_Log::warn(array('Oh noes something went wrong'));
MyCustom_Api_Log::notice(array('Oh noes something went wrong'));
As you can see, I can call the magic method with err, warn, or notice and logs my messages at that log level.
I am creating a class which I will use to store and load some settings. Inside the class all settings are stored in an array. The settings can be nested, so the settings array is a multidimensional array. I want to store and load the settings using the magic methods __get and __set, so the settings can act as class members. However, since I'm using nested methods, I can't get the __set method to work when I try to access a nested setting.
The class is like this:
class settings
{
private $_settings = array();
//some functions to fill the array
public function __set($name, $value)
{
echo 'inside the __set method';
//do some stuff
}
}
And the code to use this class:
$foo = new settings();
//do some stuff with the class, so the internal settings array is as followed:
//array(
// somename => somevalue
// bar => array (
// baz = someothervalue
// qux = 42
// )
// )
$foo->somename = something; //this works, __set method is called correctly
$foo->bar['baz'] = somethingelse; //Doesn't work, __set method isn't called at all
How can I get this last line to work?
When accessing an array using this method, it actually goes through __get instead. In order to set a parameter on that array that was returned it needs to be returned as a reference: &__get($name)
Unless, what you mean is that you want each item that is returned as an array to act the same way as the parent object, in which case you should take a look at Zend Framework's Zend_Config object source for a good way to do that. (It returns a new instance of itself with the sub-array as the parameter).
This would work:
$settings = new Settings();
$settings->foo = 'foo';
$settings->bar = array('bar');
But, there is no point in using magic methods or the internal array at all. When you are allowing getting and setting of random members anyway, then you can just as well make them all public.
Edit after comments (not answer to question above)
Like I already said in the comments I think your design is flawed. Let's tackle this step by step and see if we can improve it. Here is what you said about the Settings class requirements:
settings can be saved to a file or a database
settings might need to update other parts of the application
settings need to be validated before they are changed
should use $setting->foo[subsetting] over $setting->data[foo[subsetting]]
settings class needs to give access to the settings data for other classes
first time an instance is made, the settings need to be loaded from a file
Now, that is quite a lot of things to do for a single class. Judging by the requirements you are trying to build a self-persisting Singleton Registry, which on a scale of 1 (bad) to 10 (apocalyptic) is a level 11 idea in my book.
According to the Single Responsibility Principle (the S in SOLID) a class should have one and only reason to change. If you look at your requirements you will notice that there is definitely more than one reason to change it. And if you look at GRASP you will notice that your class takes on more roles than it should.
In detail:
settings can be saved to a file or a database
That is at least two responsibilites: db access and file access. Some people might want to further distinguish between reading from file and saving to file. Let's ignore the DB part for now and just focus on file access and the simplest thing that could possibly work for now.
You already said that your settings array is just a dumb key/value store, which is pretty much what arrays in PHP are. Also, in PHP you can include arrays from a file when they are written like this:
<?php // settings.php
return array(
'foo' => 'bar'
);
So, technically you dont need to do anything but
$settings = include 'settings.php';
echo $settings['foo']; // prints 'bar';
to load and use your Settings array from a file. This is so simple that it's barely worth writing an object for it, especially since you will only load those settings once in your bootstrap and distribute them to the classes that need them from there.
Saving an array as an includable file isnt difficult either thanks to var_export and file_put_contents. We can easily create a Service class for that, for example
class ArrayToFileService
{
public function export($filePath, array $data)
{
file_put_contents($filePath, $this->getIncludableArrayString($data));
}
protected function getIncludableArrayString($data)
{
return sprintf('<?php return %s;', var_export($data, true));
}
}
Note that I deliberatly did not make the methods static despite the class having no members of it's own to operate on. Usign the class statically will add coupling between the class and any consumer of that class and that is undesirable and unneccessary.
All you have to do now to save your settings is
$arrayToFileService = new ArrayToFileService;
$arrayToFileService->export('settings.php', $settings);
In fact, this is completely generic, so you can reuse it for any arrays you want to persist this way.
settings might need to update other parts of the application
I am not sure why you would need this. Given that our settings array can hold arbitrary data you cannot know in advance which parts of the application might need updating. Also, knowing how to update other parts of the application isnt the responsiblity of a data container. What we need is a mechanism that tells the various parts of the application when the array got updated. Of course, we cannot do that with a plain old array because its not an object. Fortunately, PHP allows us to access an object like an array by implementing ArrayAccess:
class HashMap implements ArrayAccess
{
protected $data;
public function __construct(array $initialData = array())
{
$this->data = $initialData;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
$this->data[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->data[$offset]);
}
public function getArrayCopy()
{
return $this->data;
}
}
The methods starting with offset* are required by the interface. The method getArrayCopy is there so we can use it with our ArrayToFileService. We could also add the IteratorAggregate interface to have the object behave even more like an array but since that isnt a requirement right now, we dont need it. Now to allow for arbitrary updating, we add a Subject/Observer pattern by implementing SplSubject:
class ObservableHashMap implements ArrayAccess, SplSubject
…
protected $observers;
public function __construct(array $initialData = array())
{
$this->data = $initialData;
$this->observers = new SplObjectStorage;
}
public function attach(SplObserver $observer)
{
$this->observers->attach($observer);
}
public function detach(SplObserver $observer)
{
$this->observers->detach($observer);
}
public function notify()
{
foreach ($this->observers as $observers) {
$observers->update($this);
}
}
}
This allows us to register arbitrary objects implementing the SplObserver interface with the ObservableHashMap (renamed from HashMap) class and notify them about changes. It would be somewhat prettier to have the Observable part as a standalone class to be able to reuse it for other classes as well. For this, we could make the Observable part into a Decorator or a Trait. We could also decouple Subject and Observers further by adding an EventDispatcher to mediate between the two, but for now this should suffice.
Now to notify an observer, we have to modify all methods of the class that should trigger a notification, for instance
public function offsetSet($offset, $value)
{
$this->data[$offset] = $value;
$this->notify();
}
Whenever you call offsetSet() or use [] to modify a value in the HashMap, any registered observers will be notified and passed the entire HashMap instance. They can then inspect that instance to see whether something important changed and react as needed, e.g. let's assume SomeComponent
class SomeComponent implements SplObserver
{
public function update(SplSubject $subject)
{
echo 'something changed';
}
}
And then you just do
$data = include 'settings.php';
$settings = new ObservableHashMap($data);
$settings->attach(new SomeComponent);
$settings['foo'] = 'foobarbaz'; // will print 'something changed'
This way, your settings class needs no knowledge about what needs to happen when a value changes. You can keep it all where it belongs: in the observers.
settings need to be validated before they are changed
That one is easy. You dont do it inside the hashmap/settings object at all. Given that the HashMap is just a dumb container holding arbitrary data that is supposed to be used by other classes, you put the validation into those classes that use the data. Problem solved.
should use $setting->foo[subsetting] over $setting->data[foo[subsetting]]
Well, yeah. As you probably have guessed already, the above implementation doesnt use this notation. It uses $settings['foo'] = 'bar' and you cannot use $settings['foo']['bar'] with ArrayAccess (at least to my knowledge). So that is somewhat of a limitation.
settings class needs to give access to the settings data for other classes
This and the next requirement smell like Singleton to me. If so, think again. All you ever need is to instantiate the settings class once in your bootstrap. You are creating all the other classes that are required to fulfill the request there, so you can inject all the settings values right there. There is no need for the Settings class to be globally accessible. Create, inject, discard.
first time an instance is made, the settings need to be loaded from a file
See above.
The part $foo->bar is actually calling __get, this function should (in your case) return an array.
returning the right array in the __get would then be your solution.
As has been stated, this is because it is the array stored in $foo->bar that is being modified rather than the class member. The only way to invoke __set behaviour on an 'array' would be to create a class implementing the ArrayAccess interface and the offsetSet method, however this would defeat the purpose of keeping the settings in the same object.
A reasonably neat and common work around is to use dot delimited paths:
class Settings {
protected $__settings = array();
// Saves a lot of code duplication in get/set methods.
protected function get_or_set($key, $value = null) {
$ref =& $this->__settings;
$parts = explode('.', $key);
// Find the last array section
while(count($parts) > 1) {
$part = array_shift($parts);
if(!isset($ref[$part]))
$ref[$part] = array();
$ref =& $ref[$part];
}
// Perform the appropriate action.
$part = array_shift($parts);
if($value)
$ref[$part] = $value;
return $ref[$part];
}
public function get($key) { return $this->get_or_set($key); }
public function set($key, $value) { return $this->get_or_set($key, $value); }
public function dump() { print_r($this->__settings); }
}
$foo = new Settings();
$foo->set('somename', 'something');
$foo->set('bar.baz', 'somethingelse');
$foo->dump();
/*Array
(
[somename] => something
[bar] => Array
(
[baz] => somethingelse
)
)*/
This also makes it clearer you are not manipulating instance variables, as well as allowing arbitrary keys without fear of conflicts with instance variables. Further processing for specific keys can be achieved by simply adding key comparisons to get/set e.g.
public function set(/* ... */) {
/* ... */
if(strpos($key, 'display.theme') == 0)
/* update the theme */
/* ... */
}
This may be impossible, but I thought I'd ask before I rework the whole thing…:
I have an object class that can receive "pieces", which are also objects. It works basically like this:
class myObject {
//Receives an associative array of "Piece" objects
function __construct($objects) {
foreach( $objects as $k=>$v ) {
$this->{$k} = $v;
}
}
}
I'm omitting a lot of code obviously, but I hope this gives you the idea. Basically I have a ton of different "piece" objects that do different things, and if I pass an array of them into myObject then myObject becomes a very flexible and useful class for doing all kinds of different things.
So, I could use this to create a Book object and have pieces that included a "Author Piece" and an "ISBN Piece", and those pieces would know how to validate data etc. So I might have $book with objects set to the member variables author and isbn.
This is great because I can do things like:
echo $book->author; //all Pieces have a __toString() method.
echo $book->author->firstName;
$book->author->showBio();
$book->author->contactForm();
…and so on.
Now to the point. This system works great, and one of the things that makes it great is that I can pick and choose any of these pieces that I like and stick them into an object to bind them together.
But the problem is, I don't want someone else who might use the code later to try:
$book->author = "John Doe";
…because then they'd just have a value instead of the author object. I'd like that to give them an error and instruct them to do this instead:
$book->author->setName("John Doe");
So because I don't know in advance what pieces might be in any individual object (and the entire point is to be able to have the freedom to instantly assemble any kind of object), I can't just set private $author in the class declaration.
I tried fooling around with __get() and __set() a bit, but I couldn't get it to work without compromising the functionality of the objects as they are now.
So, like I said, I know this may be impossible, but before I give up, I thought I'd ask. Is there a way to protect the property of an object after it has been created without declaring it in the class definition?
It's absolutely not impossible. You should overwrite the magic __get and __set functions
Like this:
class myObject {
protected $data = array();
public function __construct($objects) {
foreach( $objects as $k=>$v ) {
$this->data[$k] = $v;
}
}
/* your code */
public function __get($name) {
if(array_key_exists($name, $this->data)) {
return $this->data[$name];
}
return null;
}