Okay, I'm still very new to OOP overall, but I've created a few workable classes now and I'm slowly grasping the various concepts of OOP in PHP. On a project that I'm working on currently, a user can register, login/logout, and upload/delete/comment/rate images.
Currently, when a user logs in, I put most of their information (that I have retrieved from a database) inside the $_SESSION superglobal. This includes information such as their first name, username, user_id, etc.
Complementing this, I have a page called user.functions.php which has a number of miscellaneous functions that are related to the concept of a 'user', such as permissions checking, requesting additional user data, checking a successful login, registering a user, etc.
My question is how would I manage this as a User class? If I understand correctly, anything called on a page exists on that page only, so wouldn't I have to declare a
new User();
on each page where I need to perform a task relating to that user? How is that in anyway efficient? Isn't it just simpler to call a function like:
changeAddress();
instead of:
$user = new User();
$user->changeAddress();
What about critical user data such as the user_id, which I currently store in a session? Would I migrate that data inside a class too and retrieve it via a getter method?
Basically, I fail to see how the concept of a user object can improve overall code quality, if a user, which is an inherently cross-page concept, is tied to an object, which exists solely within the confines of a single document?
What am I missing here?
You are dealing with two different different concepts:
Encapusulation of user related functionality in a class.
The stateless nature of the http protocol that causes all information to be lost after a request - response cycle is complete, except for session information that might be preserved between requests through various mechanisms.
As such, the fact that you have to recreate objects or actually any variables will happen no matter what. Creating an object of some class should not cost much. Populating the members is what counts. Following a non-OO methodology would result in populating individual variables instead of members of a class with no gain in performance.
rather than putting all the information individually in the $_SESSION you can store a $user object in session so you can access the user object from any page.
$_SESSION['user'] = new User();
$_SESSION['user']->changeAddress();
Related
I'm having trouble getting my head around the best way to perfect my system. Currently, it's pretty archaic, so I'm interested to know if you have any suggestions about improving it?
User Class -> handles data and basic functions on users that are referenced on the page. The __construct takes an ID and uses the get_user() function to get the user's data with the given ID.
UserService (extends User) -> handles login, logout, etc. Is called at the top of every file to figure out if a user is logged in, and is defined as $user = new UserService(); every pageload, which presents the current user's data from User class.
UserNotifications (extends user) -> handles user notifications.
The problem that I'm having is that I have various child classes, such as the ones above, that interact with each other and I don't want them each calling an instance of the parent that may already be instantiated through another child.
Obviously that adds unnecessary queries. What's the best way to pass parent instances between child classes?
At this point, I'm a novice to OOP, and I've been stumped by this. I also apologise for the lack of clarity. I hope that you understand.
I am having singleton Database class in classes/database.php as mentioned in Singleton pattern in php and I am creating the database object in users.php using Database::getInstance() method then I am calling the same static method in accounts.php (Database::getInstance()), here I am getting new instance instead of the instance that I created in users.php. I am new to design patterns and confused a bit.
my questions are
Do we need to have persistent object to achieve singleton pattern in php?
If not then what are all the ways that we keep the single instance all over the application.
Desktop applications often use a single database connection that persists. However, in PHP and most other web languages/frameworks this should not be the case. Instead of a single long lived connection, you use a short lived connection for each page load. Rather than thinking of your application as a whole, think of the process of each page load. In PHP, no other page load knows about the other unless you persist the data in a database or in $_SESSION or other places.
The singleton pattern in PHP is one of singleton throughout the page load. Although initially it may seem like "What's the point? Its just a page load", when you get to larger applications, it becomes obvious that each page load is not an insignificant operation. In systems like Drupal, you can have 10's to 100's of database queries happening each page load
Oh, and the singleton pattern is considered Bad Practice and will bite you later. In fact, it should probably be avoided in all of your applications. A better pattern would be something like the DAO pattern or the Factory pattern. Please, save yourself and don't use singletons (I've made that mistake so many times).
As for persisting objects between page loads (which is very necessary to almost any application), that is what your database and $_SESSION variable is for. You could also use alternative caching options like APC (http://php.net/manual/en/book.apc.php). Your application should be able to re-construct what it needs to based on whatever it finds in your database. For example, a user is created. You persist that user's data to the database. Whenever someone comes around and wants to know about the user, it pulls it out of the database to re-create your user object. Another example is that a user logs in. You want them to stay logged in, so you persist some kind of variable to $_SESSION that is checked every page load to see if they are logged in and who they are logged in as. You do some checking and then load the user that they say they are logged in as from your database.
An admin decides to ban some users and gets their IDs. What would be the best way to write it in a flexible and OOP manner?
The suggestions were made without the use of data mappers in mind as I don't see myself using such an approach yet, however if deemed necessary I would highly appreciate some good examples on how to use in such a situation as I have found online examples to be lacking.
Here's what I've thought of this far:
A User object which is made from a database table such as 'Users'. This object should:
a) Have methods to fetch a row and set variables from the data stored?
b) Have the ban state stored in the same database row as username, password and email etc.?
c) Have the ban state map itself to a variable such as public $banned;?
A UserControl object which is made for handling functionality such as banning.
a) Should this object be passed a User object and grab it's ID, or just the ID as a parameter?
b) Should a new UserControl object be created per user it needs to ban or be static?
c) Should this object simply toggle the $banned and have the User object save itself afterwards, or do the logic itself; set the 'banned' column in the Users table on the row where the given ID is?
I figured that if I put all these methods into the User object it would soon become a God object with $user->hasImages();, $user->isBanned(); and $user->sendNewsFeed();, so I could need some advice on how to do this in a manner that would be flexible enough to add and remove functionality easily in an environment where a large number of users is handled and the use of collections and such is used.
Id recommend an ACL implementation. Its more than youre asking about currently but i have a hard time believing you wont need to manage access at varying levels (admin, guest, user, etc.) to varying things (forums, images, pages, etc.). By using an ACL you could use the same code for everything and a "ban" would simply be an ACL which denies access to everything.
You can take a look at the Zend_Acl component for reference but whether you need something that robust or not is your decision.
If you have a UserStatus field in the User object and represent Banned, Guest, User, Mod, Admin, whatever in that object then just see if their status is Banned.
By business model, or business objects, I mean plain old objects like a "User" with all their properties name, adress, ...; in addition to all the user properties let's say each user would have an "AppointmentBook" object, each book has a set of "TimeSlot" objects, etc.
The business model has objects with references between them, at least that's how I code a business model in Java.
Here comes the question:
To intialize my business objects, in Java, I would
fetch all of the data from DB only once during application
initialization,
map data from my DB to my business objects
store in memory (maps) and they would be shared across all the requests.
PHP's Share-Nothing-Architecture is confusing me for proper OO programming:
If I use the same logic, I would have to fetch all the objects from DB, for every request (I know I could still cache, but you don't cache all of your DB, it's not a question about caching but rather about the way of programming in PHP and its architecture).
So let's say that for one HTTP request, I just need the User properties and I don't need to access his appointment book. It would be a pitty to fetch all the data from the DB for all the objects the User makes reference to, as I just need his properties. This means that I will initialize PHP objects from my model with a lot of NULL values (NULL because of the objects contained in User that I won't load) which can later on lead to errors.
I was wondering how professional PHP developers usually use their business objects?
(I'm coming from Java)
UPDATE: It was kind of stupid to say that I would load the whole database into memory during application init in Java. What I rather meant is that, if I need to fetch a specific user, I could just load all of its data and that would be accessible through all the requests.
In PHP you do not keep all the data of your domain business model in the memory. Instead you only request from DB ( though cache, if needed ), the data you want.
Model layer in php should be built from multiple domain object and data mappers ( i assume, that part is not so different from Java ). If you need User details, then you fetch only that information from database/cache. You most likely will have a separate mapper just for dealing with user(s).
You display the information about that user, and forget about the query. Next request (when and if it comes) will require different information. Maybe you will want ContactList for that User ... then you really do not need user itself, only his user_id. Again, you let you mapper to fetch data into the domain object responsible for handling contact list, and if contact list contains User instances, then just create them, but leave in "unfetched" state (object knows only own user_id). Fetch them only if you really need to, and only the parts which you will use ins that "view".
P.S. you might have notices, I told that model later should be segmented, but quite often php developers just create single class of each DB table (which implements ActiveRecord) and call it "model". This is a result caused by Ruby on Rails influence on php framework developers, which, IMHO, is one of the worst things that has happened to PHP in past 5 years.
Your Java example implies your storing your entire databases content in memory. If your doing that, what's the point of the database? Why not just create all those object and memdump them for persistence.
If I use the same logic, I would have to fetch all the objects from DB, for every request
That's simply madness, you don't need to fetch anything, you create new instances when you need them and destroy them when you no longer need them.
So let's say that for one HTTP request, I just need the User properties and I don't need to access his appointment book.
That's easy, redesign your user. Your user needs it's properties and a property called appointmentBook which is simply an array of appointment book ids.
If you actually need those appointments you can fetch them from the database later.
This means that I will initialize PHP objects from my model with a lot of NULL values (NULL because of the objects contained in User that I won't load) which can later on lead to errors.
Not really, if this is the case your User object is too big. Make it smaller, you should load the entire user. Except of course the user has to be small enough for you to sensible load it.
If you don't want that then you can always create a UserProperties class and let every User have one. When you load the User you load the properties, but you also have an option to create the properties seperately.
Even in Java you would not load all data from the database into memory. You can however - as you write - often load more compared to short Transaction Scripts you normally have in PHP.
You models should be "clever" then to only load the data from the persistence storage that is needed to perform the requested action. This requires the object to be "clever" enough to lazy-load data probably.
This can be achieved with a Domain Model that knows enough about itself and a Data Mapper that knows enough about the storage for example.
There are other patterns as well which might suit your needs depending on the type of application, however a Domain Model together with Data Mapper is quite flexible.
An exemplary data mapper in the PHP world is Doctrine.
I am working on this app that accesses session variables in the model layer. This just seems wrong but am willing to be proven wrong. Maybe not wrong but, in most places in app, session variables are handled in controller and passed in as arguments but, in other places, the session value is just accessed. Am I wrong that this seems like bad practice?
edit:
one reason I don't like sessions in models is that it seems to make it more complex to test. Keep it as just params passsed to functions and then recordset passed back.
thx
It depends.
The way I think about this is such:
A Model represents your data layer.
most of the time that data layer will be DB Table based
The Session is just another data storage medium.
Conclusion: If the data that your model represents is stored in the Session, than it is OK to access that data from within the model
An example is a Session based shopping cart. My cart's objects are models of my session data.
Controller shd do a check weather session exist or not before using the model which uses that session inside it .
No it shouldn't. The storage type, should be apart from your business logic. For example:
I have one simple plug-in that perform the access check and put the user object on the registry. So, instead of access session, the model have access to the registry, which is well defined.
$User = Zend_Registry::get('User'); // User model object
From the theoretical point of view, everything should be accessed through data mappers. In the future, if you change from session storage to something else, you'll need to update it just in one place. Your models do not need to know from where the data came from.
If you are taking more than one path to get your data, probably this will cause some problems when your application get large.
The OOP and layered systems approach suggestion is to created specialized objects and layers and keep things simple preventing specific actions to be spread all over the code.
But again, you do not need to change that unless you see advantages.
Keep in mind that sometimes refactoring is more efficient than try to predict everything.
What's stored in the session variables? If it's simply 'logged in? Y/N', then they probably don't need to be part of the model layer. If, however, it's more complex than that, they are probably inextricably linked to your business model and should be treated as such.
The examples at the bottom of the Zend Test documentation show how to test the full MVC using a login function. Presumably you could do the same when testing models?