I am using a MVC model in php.
The controller is sending data to the model and sometimes it expects something back.
As I write my webpage I add utilities which needs more information from the model. Mostly it makes controller pass more arguments to the model interface. The problem is that model interface mostly only passes those variables to another methods, so the parameter list should be changed in a few places. This obviously is unacceptable so how should it be handled?
I think that passing the array of data isn't the best idea because it can make it uncontrollable. Anything could add anything to that.
Currently I am using another solution. I create special class to handle this. Every one of them derives from a class containing something like "getAsArray()" method. I think that creating a special class for nearly every controller need is a waste of time and resources so here is my question: What is the best solution to this problem? What are the common ideas?
Controller
public function addUser(){
$this->load->model("User");
$username = $this->someWayOfGettingData("username");
$email = $this->someWayOfGettingData("email");
$password = $this->someWayOfGettingData("password");
$this->User->insert($username, $email, $password);
}
Model
private function someMethodOperatingOnData($username, $email, $password){
$answer = $this->queryAdd($username, $email, $password);
return $answer;
}
public function insert($username, $email, $password){
return $this->someMethodOperatingOnData($username, $email, $password);
}
What's the problem: When I decide to add another information about user (e.g. gender) I have to update the list of parameters in
insert($username, $email, $password);
someMethodOperatingOnData($username, $email, $password);
queryAdd($username, $email, $password);
to:
insert($username, $email, $password, $gender);
someMethodOperatingOnData($username, $email, $password, $gender);
queryAdd($username, $email, $password, $gender);
So I have to update every function passing those variables. I could use an array:
insert($arrayOfData);
someMethodOperatingOnData($arrayOfData);
queryAdd($arrayOfData);
But anywhere anything can add or remove those variables and I don't want that to happed.
I can think of different strategies for this:
1) As you are currently doing, create a custom class, instantiate it with your parameters, pass it over. However, since parameter list keep changing, your plain-old-data classes are going to be moving targets, i.e. costly.
2) You can create a dictionary and put those parameters into this dictionary and pass it along. It is an anti-pattern in the OO world, but it could be a reasonable solution for you.
3) In Django world, one of the best practices is to pass the request altogether. This approach keeps your method signatures clean. This is more favorable compared to #2 above.
I hope it helps.
EDIT
I am not a PHP guy. Think about dictionaries as key value pairs. In PHP, arrays are actually key-value pairs. So, you can use arrays in PHP lingo, or Dictionaries in other languages like Smalltalk. So this option still holds, although the terminology I picked was not 100% correct, the idea or principal is still the same.
The following is an excerpt from the book named Two Scoops of Django section 9-2:
For many utility functions, we are taking an attribute or attributes
from the django.http.HttpRequest (or HttpRequest for short) object and
gathering data or per- forming operations. What we’ve found is by
having the request object itself as a primary argument, we have
simpler arguments on more methods. is means less cognitive overload
of managing function/method arguments: just pass in the HttpRequest
object!
EDIT2
I have just seen the edit you made to your question. So, from the example you gave, what seems to be happening is you have a user class. As time passes by, there might be a need to add more properties to the user class. For example, 20 years ago, there was no such property as MobilePhone, but now it is almost mandatory. So, naturally the best way is to add that member property to your User class. Instantiate it with proper data and pass the User object, not individual user properties Until you can instantiate a User Object, you can pass around the request Object as mentioned in #3, afterwards, you can use the User object. I believe this is your best route.
Cheers,
Related
Ok so I have my DomainObject/Model called $user and my $userDAO object.
From what I have read so far when I need to do a CRUD operation like fetch a $user object I just do:
$user = $userDAO->fetchById($userId);
and that will return my $user object.
Apparently the DomainObject is not supposed to know that the DAO object exists and vice versa but what if the user is registering and I run the $user->register() method
$user->register($firstName, $lastName, $emailAddress, $username, $password etc.);
Somewhere in that method I need to check if the chosen username has already been taken and the same with the email address.
The only way I can think of doing this is having an instance of the $userDAO object in my $user object and then doing the validation like:
if($this->userDAO->isUsernameTaken($username)) {
// the username is already in use
}
else {
// continue on
}
But that would break the rule that the DomainObject should not know about the database stuff and vice versa but I was thinking the DomainObject doesn't really know about the database stuff because all the queries are in the DAO object, it just holds a reference to an object but it does not explicitly know what goes on in that object. Am I right or wrong?
If I'm doing it the wrong way how am I supposed to run a method like $user->register() which needs to call some database queries inside of it which needs access to a data source but the DomainObject/Model is not allowed know about any data source so the experts say?
I've gone onto page numbers on Google today I thought I'd never have to go onto and I still can't find any very solid real life examples of what to do and I'm starting to go mad because it's slowling down everything.
Any help would be appreciated. Thanks.
If you think of the architecture of your application in terms of a layered architecture, you need to build another layer on top of Domain Layer and DAO layer, leaving these two layers the way they are. This new layer is called service layer, or application layer by some. The job of this service layer is to perform use cases, such as "registering" a new user. For example, one class in service layer may be UserService which peforms use cases related to user,
class UserService {
private UserDao userDao;
// constructor
userSercie(){
}
// registers a user
register($firstName, $lastName, $emailAddress, $username, $password etc.) {
$user = $userDAO->fetchByName($username);
if($user != null) {
// the username is already in use
}
else {
// continue on
}
}
// other service methods such as
}
Why do we need service layer? It is exactly in response to problems like yours, that we need service layer. This way you can see that we can keep separation of concerns between different module.
I'm a little confused as to what is going on here, it looks to me like a method is calling itself? I'm trying to learn about Magento's models. I was working my way back from a helper (catalog/category) and I got to a call on this method "GetCategories". I don't know whats going on here. If anyone could shed light on this code snippet I greatly appreciate it.
getCategories ( $parent,
$recursionLevel = 0,
$sorted = false,
$asCollection = false,
$toLoad = true
){
$categories = $this->getResource()
->getCategories($parent, $recursionLevel, $sorted, $asCollection, $toLoad);
return $categories;
}
Not much to add to #hakra's answer. Just a portion of Magento-specific logic.
So to work with Magento models you should know, that Magento has 2 types of Models: normal models, and resource models (we can call assign Blocks to the models too, as a view models - but that is more connected to the V part of MVC).
The resource models were created as a DB adapters that contain only DB-related logic, and often are connected to some DB table, hence contain the logic for CRUD operations with that table. So you'll see smth like this regularly - for the simplicity someMethod is a part of normal model, but since it contains DB-related logic, all the implementation of the method was moved to the resource model, so the body of someMethod in the regular model will be something like that:
public function someMethod($args)
{
return $this->getResource()->someMethod($args);
}
It is hard to say for the code you've posted. Even both methods share the same name (getCategories) it must not mean that they are of the same class or even object.
If you want to find out you would need to compare:
var_dump($this === $this->getResource());
Apart from that, it is also common in programming recursion that a method calls itself, hence recursion. However for that chunk of code, it would run against the wall.
So technically speaking I would do the assumption that in your example this is not the exact same object method.
Please take note that this answer is independent to Magento, it's just how PHP works generally.
How do people design their service layer interfaces?
I'm programming a large web application (in PHP) and we're using MVC, and programming thin controllers e.g. (pseudo code follows)
public savePersonAction() {
$input = filter($_GET);
... input validation ...
$result = $this->_service->savePerson( ? );
... etc
}
Should savePerson in the service take an argument of the entire $input structure or context (in PHP, an associative array)?
E.g. this -
public function savePerson(array $input) {
or should one separate out all the input fields and provide a "hard" interface e.g.
public function savePerson($title, $firstName, $lastName, $dateOfBirth, ... etc.. for many more) {
Thanks.
Paul
If you're going to follow the MVC spirit your savePerson method should not accept the raw input. It shouldn't be directly coupled with the format of the data coming from the ui. Instead it should accept input defined in the terms of your service domain, like a "person" object. (This could just be an associative array like Cobby suggested). It would be the job of the controller's action method to map the raw input to the format required by the service.
The benefit of this extra translation step is that it isolates your service (model) from the ui. If you implement a different ui, you don't need to change the service interface. You just need to write new controllers (and views, of course).
While your example like savePerson($title, $firstName, $lastName...) is the right idea, it's usually a bad sign when you have methods with more than 2 or 3 arguments. You should be able to group related arguments into some kind of higher-level object.
My MVC applications are structured like this:
Controller -> Service -> ORM/other library
To answer your question, typically in your controller you will be getting form data as an array, i.e. $form->getValues() or something similar. In terms of maintainability it's best if your Services except arrays as arguments, that way if you add another field to a form, you only have to update the form and the service, your controller can remain untouched and still work.
So I think go with your first example:
public function savePerson($personArray);
Furthermore, you shouldn't need a "hard" interface because your form library will take care of validation/filteration/sanitization so we can assume that the associative array will be valid, plus the method definition will get ridiculously long with named parameters.
I would separate out all the input fields and provide a "hard" interface in Service e.g.
public function savePerson($title, $firstName, $lastName, $dateOfBirth) {...}
Its cleaner and there are no assumptions needed.
In the end, I got this function. I don't know whether it's normal or not.
function user_registration($user_name, $user_email, $user_pass, $address,
$city, $postalcode, $country, $phone, $mobilephone)
How and why can I improve this?
You could either pass an array with all variables packed nicely together, or just make a "User" class and add all properties via setters and do the validation in the end with a dedicated method:
class User {
public function setName($name) {
$this->name = $name;
}
[...]
public function register() {
//Validate input
if (empty($this->name))
$this->errors[] = "ERROR, Username must not be emtpy";
//Add the user to the database
//Your SQL query
return empty($this->errors);
}
}
$user = new User();
$user->setName("Peter");
$success = $user->register();
if (!$success)
echo "ERRORS OCCURED: ".print_r($user->errors, true);
A solution would be to only have one parameter, that can contain several pieces of data -- like an array.
Your function could be defined this way :
function user_registration(array $data) {
// work with $data['name']
// and $data['email']
// ...
}
And you'd call it like this :
user_registration(array(
'name' => 'blah',
'email' => 'test#example.com',
'pass' => '123456',
// and so on
));
Nice things are :
You can add / remove "parameters" easily
The "parameters" can be passed in any order you want
Not so bad things are :
No hint while typing in your IDE
No documentation (like phpDoc)
I personally don't think it has too many parameters. From looking at the functions definition it is clear what you require as input which wouldn't be so obvious if called with an array.
"If it aint broke don't fix it!"
When you look at your argument names, you cannot but notice that they can be grouped into three different groups:
User Data: $user_name, $user_pass
Address Data: $address, $city, $postalcode, $country
Contact Data: $user_email, $phone, $mobilephone
Consequently, you could apply Introduce Parameter Object:
Often you see a particular group of parameters that tend to be passed together. Several methods may use this group, either on one class or in several classes. Such a group of classes is a data clump and can be replaced with an object that carries all of this data. It is worthwhile to turn these parameters into objects just to group the data together. This refactoring is useful because it reduces the size of the parameter lists, and long parameter lists are hard to understand. The defined accessors on the new object also make the code more consistent, which again makes it easier to understand and modify.
If you dont want to do OOP you could group the arguments into arrays as well but you'll lose all the type benefits then. I will just assume you don't mind using objects. So, after applying the Refactoring you'll end up with
function user_registration(User $user, Address $address, Contact $contact)
Looking at that parameter list should make you notice that Address and Contact likely belong to User in the first place, so you could consider changing the function signature to just
function user_registration(User $user)
and then call it like this:
$user = new User('johndoe', 'secretsauce');
$user->setAddress(new Address('Doe Street', 'Doe Town', 12345, 'Neverland'));
$user->setContact('jdoe#example.com', '+123 12345', '+123 54321');
user_registration($user);
We could probably make username and password into a Credentials object as well and then just do
user_registration(new User($credentials, $address, $contact));
By requiring the data in the ctor we make sure newly registered users do have all these information. We could argue whether we need Address and Contact for registering users though, so Setter injection might be good enough here:
$user = new User(new Credentials('johndoe', 'secretsauce'));
$user->setAddress(new Address('Doe Street', 'Doe Town', 12345, 'Neverland'));
$user->setContact(new Contact('jdoe#example.com', '+123 12345', '+123 54321'));
user_registration($user);
However, user_registration as a separate function in global scope is misplaced. By GRASP's Information Expert principle, methods should be on the objects that have the most information to fulfill the responsibility. This improves Cohesion and reduces Coupling. In other words:
$user = new User($credentials);
$user->setAddress($address);
$user->setContact($contact);
$user->register();
One problem with the User class now is that it contains the password. The password is only ever needed to authenticate the user against the authentication service. We could argue about the username, but the password definitely should not be part of the User object at all. So you should do something like
$user = new User;
$user->setAddress($address);
$user->setContact($contact);
$user->register($credentials);
and when register() is called, it will only use the credentials to delegate insertion of a new user into the user storage. But it will not retain them in the actual User instance.
Finally, you might want to add a Simple Factory or Builder pattern to encapsulate the creation of the User to simplify the aggregation of the various instances. Or you might want to introduce a Repository pattern and move the register() method there. This is beyond scope for this question though.
As a general rule of thumb (not as a steadfast rule), anytime you have to ask "Does this function have too many parameters?" -- the answer is yes. Your intuition is telling you something that your brain just hasn't yet been able to work out.
In this particular case, the first thing that comes to mind is that your user cred.s should be checked first (does the username already exist? is the pw complex enough) and your user details should be added separately, probably using an object or array.
One way is to pass an array as parameter to this function and put all info in that array:
function user_registration(array $user_info)
{
// process $user_info;
}
Function (method) without any parameters is best. Function with one paremeter is better than function with 2 parameters. Function with 2 parameters is better than function with 3 parameters and so on.
#Florianh has given a perfect solution how your code can be improved. With this comment, I would like to elaborate on the "why" part from a system design perspective.
From an Object Oriented perspective, other objects should be able to manipulate attributes. That is why attributes should never be defined as "public". They should be "private" e.g.:
private var $name;
The reason for this is when other objects would manipulate this variable, the correct working of the object is at risk. Methods, on the other hand, can be defined publicly:
public function register()
Accordingly, the manipulation of the attributes will happen through the appropriate methods. A method can be used to also evaluate the correctness of operations on the attributes.
There are two operations that can happen: reading the current value of an attribute by using Get methods and saving a new value of an attribute by using Set methods.
A good practice would be to implement a get method for each class attribute. Each attribute that can be modified, should have an appropriate set method as well.
Sometimes it is better to not implement a get/set method (e.g.: showData()) after all. This is because the usage of getters and setters within a particular class may possibly lead to a reduced performance. However, this means that when changing or implementing the class, one must be careful when trying to save false information and as a result putting the integrity of the class at risk.
Now, consider the fact that you decide to use only one phone number instead of both a phone- and mobile number. When the mobile number becomes deprecated, your main program still remains the same. All you have to do is change/remove one method. The advantage of using getters and setters is an increase in adaptability and maintainability.
I make array of keys like
$fields = array('field1', 'field2');
function register (array $values, array $keys)
{
$data = array();
foreach ($keys as $one)
{
if (isset($values[$one])) $data[$one] = $values[$one];
}
// or you can use array functions like array_flip and after - array intersect
}
I'd make it this way
fields=explode(",","name,surname,lastname,street,city,region,zip,country");
user_registration($fields);
Because I am sure these variables coming from $_POST
The thing is that you have classes and then you have the database data. When you create an object how do you set the objects properties to contain the data in the database ?
I saw something like this and I'm wondering if this is really the best way to do it. I'm sure this is a fairly common issue, but I don't know what are the most accepted solutions on how to handle it.
In this example when the object is created you pass an id as a parameter and then you run a query to the database with the id and you assing the returned values to the object properties. I don't have much PHP experience and haven't seen this used much.
Is this an acceptable way to achieve this purpose ? Is there a better or more accepted way ?
public function __construct($id = null){
if($id != null){
$sql = "SELECT *
FROM users
WHERE user_id = $id";
$res = Db::returnRow($sql);
// $res contains an associative array with database columns and values
if($res){
$this->user_id = $res['user_id'];
$this->user_name = $res['user_name'];
//and so on...
}
}
}
Could somebody provide some sample code or pseudocode to illustrate what is the correct way to do this ?
It could be an acceptable way for a homework maybe. But architecturaly it is not.
Your class that is representing your business data (a user in your example) must be loosely coupled with your database access logic. In the end the PHP class acting as a user should not be aware that the data come from a database, a file or any other resource. Following that you will be able to reuse your user php class in other projects without having to change anything to it! If you have your data access logic inside it you are stuck.
Conclusion: I would suggest to read some resources on Design Pattern (in your situation take a look at DAO pattern) ;) Hint: the one from Head First series is extremely accessible and enjoyable.
You could create a function to do this for you automatically, by looping over the associative array's key/value pairs. Or you could look into using an ORM library.
Yes, you can semi-automate this by having a parent class all objects inherit from. On load, it queries, "SHOW FIELDS FROM [my tablename]" and populates an associative array with the names. If an id has been passed in, it looks for a valid object in that table with that id and assigns the values to the array.
Side note: don't pass your id directly into your query like that. Parametize the sql and wrap a function around any user input to sanitize it.
If it's mysql, you can just do:
$obj = mysql_fetch_object($query);
PDO the ability to use arbitrary classes as the target for a fetch, but beware that they assign the variable data before running the constructor:
$pdo->query($stmt, PDO::FETCH_CLASS, "MyClass", array('foo'=>'bar'));
...where the final parameter contains arguments for your class constructor.