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.
Related
I am quite new to DDD and have some fundamental problems that i cant really understand.
Consider we have a User entity. A User has some Friend's (one to Many).
There are 2 possible solutions to fetch the friends.
Solution 1:
user.getFriends()
The Problem here is that all the friends have to be loaded or I am forced to use a Proxy.
If i use a Proxy it feels for me like cheating because the entity must not have an instance of the Repository. Can the Proxy have one?
Solution 2:
Avoid the getFriends Method and load the Friends Based on the User Repository, like:
userRepository.getFriendsOf(user)
This could sometimes be a good idea, but where to stop here? Whats about an AddressRepository for accessing user Addresses, or ?
I think you should look at it from perspective of Aggregate Roots.
In case of user.getFriends() - the user seem to be AR with associated friends which I don't think is correct. The definition of AR says "that we treat as a unit for the purpose of data changes". Friends are outside of the Boundary of user AR.
Considering the above the other option seem better - userRepository.getFriendsOf(user)
I'm not sure that either of them are correct. I think from a DDD point of view, the code is supposed to use the Ubiquitous Language of the domain. In that regard, getters are frowned upon.
Why are you getting the friends of a user? The public methods of User should reflect the ubiquitous language.
For example if your domain is a social networking site, users should have methods to update status which will internally notify friends etc. How the User knows who its friends are or how they are queried is hidden as an implementation detail.
One thing to keep in mind is that you shouldn't use your domain model for querying. So you do not want to be lazy-loading or applying any other fetching strategies.
You also need to identity your aggregates. An aggregate is loaded in its entirety from the data store.
In your case if a Friend class can only ever exist in the context of a User then it is part and parcel of the User and its life-cycle is linked to that of the User. That means that when the User is deleted (conceptually, as one probably would not want a hard delete) then the associated Friend instances are deleted also.
So if a Friend represent the association between users then it probably only contains the User Id. So you could go with a list of Ids in that case. This would depend on your domain.
I would guess that you very rarely would need to load the entire list of User instances that are friends of another user. If you need related data you could implement a light-weight query layer.
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();
I'm looking for best practices to fetch users created content.
I have $user object form 'security.context' and I need to get single record created by this user by some $record_id,
so what I should do?
$this->getDoctrine()->getRepository('AcmeRecordBundle:Record')
->findOneBy(array( 'id' => $record_id, 'user' => $user->getId() ));
This doesn't look good to me, because I have lot's of information that needs to be fetch looking for user too(to don't let other users try get it by some id). And for any content( personal photo, some other private content) I have to pass 'user' => $user->getId() ?
Or it's better to create UserRepository with all these functions? getRecordById($id), getPhotoById($id), getPrivateInformationById($id), etc.
I was working with Rails a little, and there I was able to define current_user method
def current_user
return #current_user if defined?(#current_user)
# ....
end
and then just use it as
current_account.records.find(params[:id])
is there any possibility to make it work like this with Doctrine2 and Symfony2? Like
$user->getRecords()->find($recordId)
In any situation you have to specify user which you pass to your function, which deal with the fetching logic inside custom repository as specified in official documentation for Doctrine.
Of course you have to pass the user's id for the "WHERE" sql clause, just because ROR did it magically behind the scenes (which is a very bad practice imo), doesn't mean it didn't do it at all.
As for the other matter, both solutions are ok:
Fetch data from the particular repository, and pass the object id + the user's id, or:
Create methods which internally get user's id and put them in queries
And remember that user's id is fetched only once during the request, so don't worry about getting it from the security context too much.
You need to implement Symfony 2 ACL features. This allows you to specify ownership for "domain objects" (individual instances of DB classes) and what kind of access users have on a domain object. Then you can use for example the JMSSecurityExtraBundle and implement access controls based on object ownership. Once implemented your users won't be able to modify each other's objects (by parameter manipulation) and you won't need the additional parameter in your queries.
Here are a few relevant links:
Access Control Lists (ACLs)
JMSSecurityExtraBundle
Personally I found the repository classes to bloat things a bit in a small to mid-size application. Not sure what your approach is, but most everything I've read (and what I went w/ in a recent Doctrine 2 app) was to have a 'service' layer which manipulated the entities. This is b/c in D2, implementing save / delete etc in the entities undermines the purpose of the system which is to alleviate knowledge of persistence from the entities and treat them as Plain Old Php Objects (TM) ;)
The thing that looks odd to me about your query is passing an primary key id and a User id to fetch a User. Seems to me like the pk of the User table would be the user id, or at the very least if the user id isn't the pk (not sure why that would be) you should be able to get the records w/ just the pk. Here's the method to fetch a User object in my system
/**
* #param int $iId user id
*
* #return object
*/
public function fetch($iId)
{
return $this->_oEm->find('AwesomeApp\Entity\User', $iId);
}
The current user sort of function you're looking for should be related to the session in your application. In zf I've created a session handler that persists the doctrine User object to session storage, then when the session is read I re-attach the User object to the Entity Manager. You probly want to do something similar in sf, then a 'getCurrentUser' call would return the same User object as pulling it from the database. Storing a User object in the session prevents the need to go back to the database for it on every page load, for example if you just stored the User id in the session.
At the end of the day you're 'supposed' to put complex select queries into repositories, but this is obviously left to User discretion when it comes to best practices. In this case, when you have just a pk, I'd say there's no point to writing a repository class.
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.
So I am a little confused on the object oriented part of PHP. Right away I will apologize for the fact I know very little about PHP and databases.
My question is when you are making, say, a database to hold users in it, why would you want to make a class/object for that user when you can just pull info from the database?
Also, if you were to make a object/class where is the data for the objects stored? For example, if I have a class with a username and email, and I make that object, were does it get stored?
Thanks for taking your time to help a learning noob!
My question is when your making per
say a database to hold users in it,
why would you want to make a
class/object for that user when you
can just pull info from the database.
You make objects to abstract away specific functionality. What happens if you move to, say, Microsoft SQL Server (hypothetically speaking)? Rather than update your entire site, you just edit the implementation of the object.
Also if you were to make a
object/class where is the data for the
objects stored? Like a class with a
username and email, and I make that
object, were does it get stored.
The same place as any other variable.
There are a LOT of reasons why you want to use some abstraction on top of just raw database access in any reasonably large software system. If you're looking at an Object Oriented approach you should consider that one of the core ideas of the Object Oriented paradigm is that an object encapsulates both data and logic that acts on that data.
Let's take a concrete example. Say that a part of your application (the UI) needs to display user information, including a nicely formatted user name. In an OO world you could have a User object which would store a local copy of the data in the database, and expose methods like getFormattedName(), or something similar. Now the rest of your application can use that code without needing to know about the database, or even how the name is formatted. On the other hand if you were just pulling data directly from the database then the UI part of the application (which doesn't really care about databases) still has to know itself how to get information about the user from the database, and how to format the users name nicely.
To put it simply, there are logic not captured in a database table but related to the entry. The database only stores the raw data. How the data is used and how it interacts with the rest of your application should be captured in your object methods.
You're missing a fundamental of object-oriented design. Ignoring inheritence entirely, Objects combine information/data and functions/procedures/operations into a single unit called an object. This object performs operations (methods/behaviors/functions/procedures) and has attributes. A database will not have the entire set of operational/procedural information. By design, a database will only contain data, and know nothing of how the data can be used or what the data does.
Databases store data in a tabular fashion which is designed to be speedy. Objects are so much more flexible; they can be trees, they can be lists, they can be widgets, or anything else out of a million things. They can represent presentation, data, or structure. And sometimes they are even faster (when it's easier to calculate a value on the fly rather than retrieve it from a database). Databases are very powerful and important but are only appropriate for a small subset of the tasks that a web application performs. The rest are made easier by objects.