Jump to content

Using several methods or class constants


DaveyK

Recommended Posts

Hey Freakers,

 

I have a question for you. Its about PHP OOP. Basically, I am rewriting my users class and I have a method to fetch info (surprise). However, the amount or kind of info I require is different for some situations. Sometimes I only need basic info (id, email, avatar) and sometimes I need all of it (id, email, full name, avatar and other relevant information). The question here is, how do YOU guys do this. I always used seperate methods:

<?php

class Users
{
  public function fetchInfo ($user_id)
  {
    // fetch user id, full name, email and other relevant info
  }
  
  public function fetchInfoMin ($user_id)
  {
    // fetch a small amount of required info
  }
}

?>

This works fine, but I use cases where the amount of info to be fetched is determined by method variables (often constans) and I wonder how that would work. I imagine something like this:

<?php

class User
{
  const $fetch_all = 'fetch_all';

  public function fetchInfo($user_id, $fetch_all = self::FETCH_ALL)
  {
     // set the sql based on the $fetch_all variable.
  }
}

?>

I wrote that out of the top of my head and frankly I have no idea how that would work, unless just for a lot of if statements changing the SQL and afterwards also the data manipulation on the function itself.

 

How do you guys do this? What is best practise?

Link to comment
Share on other sites

A User class would generally be modeled on your data, with each database column being represented by a public property or (better still) a getter method.

 

A fetchInfo() method doesn't really make sense.

Link to comment
Share on other sites

It took me a while to understand OOP and I'm always learning something new.  Just remember all you attributes have to correspond to the database table columns:

 

Fore example a User class attributes might look something like:

 

 

protected $id = null;
protected $userType = null;
protected $username = null;
protected $email = null;
protected $pass = null;
protected $dateAdded = null;

You can even have specialized methods(functions)

 

// Method returns a Boolean if the users is an administrator:
function isAdmin() {
return ($this->userType == 'admin');
}

And I think the following will really clear this up (This is part of my login.php file):

// Check against the database:
$query = 'SELECT id, userType, username, email FROM users WHERE email=:email AND pass=SHA1(:pass)';
$stmt = $pdo->prepare($query);
$result = $stmt->execute(array(':email' => $email->getValue(), ':pass' => $password->getValue()));


// Try to fetch the results:
if ($result) {
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$user = $stmt->fetch();
}

 

 

 

 

 

 

Link to comment
Share on other sites

I'd recommend making use of the magic methods, particularly "__get()" and "__set()" as they're extremely useful for managing your variables when it comes to modeling your database; this essentially builds upon what trq said.

Link to comment
Share on other sites

A User class would generally be modeled on your data, with each database column being represented by a public property or (better still) a getter method.

 

A fetchInfo() method doesn't really make sense.

 

How doesnt it? I need a function/method to return me the info I need to display the user profile I am currently viewing. In normal php, I would just use a fetch_user_info () function. Would you mind clearifying and showing me how it SHOULD be done rather than critizing how it shouldnt be done.

 

 

It took me a while to understand OOP and I'm always learning something new.  Just remember all you attributes have to correspond to the database table columns:

 

Fore example a User class attributes might look something like:

 

 

protected $id = null;
protected $userType = null;
protected $username = null;
protected $email = null;
protected $pass = null;
protected $dateAdded = null;

You can even have specialized methods(functions)

 

// Method returns a Boolean if the users is an administrator:
function isAdmin() {
return ($this->userType == 'admin');
}

And I think the following will really clear this up (This is part of my login.php file):

// Check against the database:
$query = 'SELECT id, userType, username, email FROM users WHERE email=:email AND pass=SHA1(:pass)';
$stmt = $pdo->prepare($query);
$result = $stmt->execute(array(':email' => $email->getValue(), ':pass' => $password->getValue()));


// Try to fetch the results:
if ($result) {
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$user = $stmt->fetch();
}

 

I dont really understand the advantage of this. How do you set the user id then. and how do you get the info out of the method if you dont return it?! I dont know OOP and its all just me trying to do some stuff without anyone helping me. Ive never been schooled in PHP OOP so forgive me for not understanding this.

 

EDIT: I replied inside the quote. Not too bright.

Edited by DaveyK
Link to comment
Share on other sites

You're getting confused between your data model layer and your database layer; arguably one-in-the-same. 

 

The logic for interacting with the database, i.e. the logic to actually run a query, should ideally be in a class of its own; this acts as a portal for all queries. A data model class wraps, normally, a single row from your query into an object. You can then pass this object between other objects to keep the information contained. If you want to pass multiple rows you can create an iterator and pass the iterator around.

 

Or perhaps have an iterator contained within the data model that, when iterated through, sets the fields to their appropriate values within the data model.

Edited by cpd
Link to comment
Share on other sites

Strider64, on 16 May 2013 - 2:31 PM, said:

 

// Check against the database:
$query = 'SELECT id, userType, username, email FROM users WHERE email=:email AND pass=SHA1(:pass)';
$stmt = $pdo->prepare($query);
$result = $stmt->execute(array(':email' => $email->getValue(), ':pass' => $password->getValue()));


// Try to fetch the results:
if ($result) {
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$user = $stmt->fetch();
}

 

This demonstrates the apparent problem you face when you use OOP and an RDBMS without an ORM. This code will work until you have an object with relations to other objects. Suppose User has a Role object. So you would need 2 queries or a JOIN but then you will have to manually write out the colums you need, possibly aliasing a few and then mapping them to the appropriate objects. This becomes really tedious real soon which is why everyone uses an ORM. The above code also assumes all your properties are public which is not good.

 

For PHP there are multiple ORM's available the most popular being Doctrine and Propel.

Edited by ignace
Link to comment
Share on other sites

How doesnt it? I need a function/method to return me the info I need to display the user profile I am currently viewing. In normal php, I would just use a fetch_user_info () function. Would you mind clearifying and showing me how it SHOULD be done rather than critizing how it shouldnt be done.

$user->fetchInfo(1);
What info? Addresses? Girlfriends? And where is it getting it from? Your User object shouldnt be aware of a database, like so:

 

class User {
  private $id;
  private $email;
  private $pass;
  
  public function getId() { .. }
  public function getEmail() { .. }
}
To get this User from the database I would do:

 

$user = $em->find('User', 1);
print $user->getEmail();
To change his e-mail address and store it into the database:

 

$user->setEmail('my-brand-spanking-new@gmail.com');
$em->persist($user); // assuming explicit strategy
$em->flush();
The advantage to this way of programming is that I can use a User object in other parts of my application without needing a database:

 

$from = new User();
$from->setEmail('bert@bert-en-ernie.nl');

$to = new User();
$to->setEmail('ernie@bert-en-ernie.nl');

$messageSender->sendMessage($from, $to, 'Je hebt een banaan in je oor!');
Edited by ignace
Link to comment
Share on other sites

This demonstrates the apparent problem you face when you use OOP and an RDBMS without an ORM. This code will work until you have an object with relations to other objects. Suppose User has a Role object. So you would need 2 queries or a JOIN but then you will have to manually write out the colums you need, possibly aliasing a few and then mapping them to the appropriate objects. This becomes really tedious real soon which is why everyone uses an ORM. The above code also assumes all your properties are public which is not good.

 

For PHP there are multiple ORM's available the most popular being Doctrine and Propel.

 

I see. I had to google search a bit but I think I get it. At this point I manually write all queries (and columns) I need in seperate functions/methods. I just dont know any better.

 

$user->fetchInfo(1);
What info? Addresses? Girlfriends? And where is it getting it from? Your User object shouldnt be aware of a database, like so:

 

class User {
  private $id;
  private $email;
  private $pass;
  
  public function getId() { .. }
  public function getEmail() { .. }
}
To get this User from the database I would do:

 

$user = $em->find('User', 1);
print $user->getEmail();
To change his e-mail address and store it into the database:

 

$user->setEmail('my-brand-spanking-new@gmail.com');
$em->persist($user); // assuming explicit strategy
$em->flush();
The advantage to this way of programming is that I can use a User object in other parts of my application without needing a database:

 

$from = new User();
$from->setEmail('bert@bert-en-ernie.nl');

$to = new User();
$to->setEmail('ernie@bert-en-ernie.nl');

$messageSender->sendMessage($from, $to, 'Je hebt een banaan in je oor!');

 

Regarding the fetchInfo() method question: Yes, information. If that were a function, I would make it return all information, something like: id, user name, email, status, avatar and possibly more info based on the users data. I just simply write an sql query and return the data.

 

Regarding the rest. I am terribly sorry but I just dont follow. First:

$user = $em->find('User', 1);
print $user->getEmail();

what is $em ?! I think you are assuming I know what this is, while in face I dont have the slighest clue.

$from = new User();
$from->setEmail('bert@bert-en-ernie.nl');

$to = new User();
$to->setEmail('ernie@bert-en-ernie.nl');

$messageSender->sendMessage($from, $to, 'Je hebt een banaan in je oor!');

Again, I dont understand the use of this. First of all, no one likes a banana in ones ear.

Second, you already have the $from variable, so why would you add it to a class and then fetch it from the same class. What am I missing here?!

Also, "without needing a database". Do $from and $to just magically appear? Unless you fetch from a form (get, post) or from a session or something, that information is already likely fetched from a database.

 

 

here is an example of my User.class.php file.

class User
{
  private $logged_in_user_id;
  
  public function __construct ($user_id = false)
  {
    $this->logged_in_user_id = $user_id;
  }
  
  public function fetchInfo ($user_id = false)
  {
    if ($user_id === false)
    {
      $user_id = $this->logged_in_user_id;
    }
	
	global $pdo;
    
	$user_id = intval($user_id);
	
	$statement = $pdo->prepare("SELECT
                `id`,
                `email`,
                `status`,
                `password`,
                `salt`
              FROM `users`
              WHERE `id` = ?");
	
	$statement->execute(array($user_id));
	
	return $statement->fetch(PDO::FETCH_ASSOC);
  }
  
  public function setLoggedInUser ($user_id)
  {
    $this->logged_in_user_id = $user_id;
  }
}

I assume that its about 100% between "class User {" and "}" is wrong, but please keep in mind that im just doing what I think works and I am coming here because I want to improve my code and expand my knowledge, so please help me...

Edited by DaveyK
Link to comment
Share on other sites

From what you've said it sounds as though you'd merely return an array of data detailing the user information. A good object oriented design involves encapsulating data in the correct data structures with the correct visibility. For example the ID field of a user could be considered a property that shouldn't be overwritten and therefore you only provide a getter for that attribute. In contrast you could have the first and last name of a user that you want to be able to overwrite so you can write a getter and setter.

 

You can then appropriately manipulate the state of the object and commit it to the database via the ORM.

Edited by cpd
Link to comment
Share on other sites

what is $em ?! I think you are assuming I know what this is, while in face I dont have the slighest clue.

The $em variable would hold an instance of something known as an Entity Manager. That is basically a class that knows about your different entities (Users, Messages, Blogs, Photos, whatever) and handles the creation of those entities. Something like $em->find('User', 1) would be asking the entity manager that you want it to find the User entity with ID #1 so what it would do is generate the appropriate query to pull the information from the database, create a new instance of the User class, and then set all the properties of that class with the values from the database. How it knows what to do generally comes down to two possibilities:

A) Everything follows a strict naming standard. Your class names match up with your table names. Your property names match up with your column names. And so on for any other cases

B) or everything is configured via some configuration file which defines which entities your application has, what class is used to represent them, which properties match up with with columns, etc.

 

The second approach is more flexable generally but also more work in the initial setup. Often there is a script you can use that will apply the principals of method a to automatically generate a configuration for method b which can then be tweaked if necessary.

 

 

Second, you already have the $from variable, so why would you add it to a class and then fetch it from the same class. What am I missing here?!

The point is about having consistant interfaces and being able to use them easily. In the example above, there is some class that is used for sending email messages between users. It accepts two User objects and the message as it's parameters and then acquires the user's email (and potentially other details, ie user ID, names, etc) from those user objects by calling the appropriate properties (ie, $from->getEmail, $from->getName, etc). By having your code always deal with user objects you then always have a nice consistant way to access a users data.

 

 

Also, "without needing a database". Do $from and $to just magically appear? Unless you fetch from a form (get, post) or from a session or something, that information is already likely fetched from a database.

One prime example of not needing a database would be if you do Unit Testing. In such an environment you would create "fake" data and use that to do the test. So in the example you'd create two User objects with fake details and pass that to your messaging object in order to test that it is working properly. Rather than go through a huge hassle of setting up a DB and anything else that might be neccessary for that to work you just create two simple objects, set their details, and go.

 

 

Now, all of that said/explained:

How do you guys do this?

Our application is similar to what you have said where we have methods that return an array of all the various information for a given entity. For the most part I've gone the route of using a single method, GetDetails, with parameters to control which information to load. For example we have a Student class that returns details about a student. Whether it loads additional details about their enrollments though is a parameter. The method is defined as

public static function GetDetails($studentId, $loadEnrollments=false);

 

When all we need is things like their name, email, etc then we skip loading the enrollments. If we need the enrollment data then we just set the second parameter to true in order to have it pull that information as well. As far as which is best, separate methods vs parameters, it doesn't really matter. Either will work fine, neither is really fantastic. Something more advance like describe in previous posts would likely be best just from a maintainability/testability point of view, which is the most important point of view.

Link to comment
Share on other sites

Okay, I can see that this might be advantageous if you implement it from the beginning, but I am writing an admin panel to a site and I only just started using PDO sql where the normal site uses MySQL. Implementing an ORM like doctrine or propel is a step to far for me. I am only just getting used to PDO and having to learn that ORM as well will set me back way too long.

 

The thing I find interesting in your post tho is that $loadEnrollments=false. What do you do with that variable to alter the sql? This was my original question.

Link to comment
Share on other sites

The thing I find interesting in your post tho is that $loadEnrollments=false. What do you do with that variable to alter the sql? This was my original question.

In my particular case it just does a second query and then merges the results together in the code. Ie:

public static function GetDetails($id, $loadEnrollments=false){
   $stmt = /* query to get basic details */;
   $details = $stmt->fetchAll();
   if ($loadEnrollments){
       $stmt = /* query to get enrollment data */
       $details['enrollments'] = $stmt->fetchAll();
   }

   return $details;
}
Depending on your needs though you could do anything from just change an array of fields to be selected to simply using two different querys.

$fields = array('a','b','c');
if ($loadEnrollments){ 
   $fields=array_merge($fields, array('d','e','f'));
}

//or
if ($loadEnrollments){
  $sql = 'The big query';
}
else {
  $sql = 'The smaller query';
}

$stmt = $db->query($sql);
Link to comment
Share on other sites

 

It took me a while to understand OOP and I'm always learning something new.  Just remember all you attributes have to correspond to the database table columns:

 

Fore example a User class attributes might look something like:

 

 

protected $id = null;
protected $userType = null;
protected $username = null;
protected $email = null;
protected $pass = null;
protected $dateAdded = null;

You can even have specialized methods(functions)

 

// Method returns a Boolean if the users is an administrator:
function isAdmin() {
return ($this->userType == 'admin');
}

And I think the following will really clear this up (This is part of my login.php file):

// Check against the database:
$query = 'SELECT id, userType, username, email FROM users WHERE email=:email AND pass=SHA1(:pass)';
$stmt = $pdo->prepare($query);
$result = $stmt->execute(array(':email' => $email->getValue(), ':pass' => $password->getValue()));


// Try to fetch the results:
if ($result) {
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$user = $stmt->fetch();
}

 

 

 

 

 

Try not to use transaction script unless you do not have a choice, instead apply the data mapper design pattern to make the best use of OOP. Transaction script is not even OOP to begin with, it is an anti-pattern with objects written in procedural code. 

Link to comment
Share on other sites

In my particular case it just does a second query and then merges the results together in the code. Ie:

public static function GetDetails($id, $loadEnrollments=false){
   $stmt = /* query to get basic details */;
   $details = $stmt->fetchAll();
   if ($loadEnrollments){
       $stmt = /* query to get enrollment data */
       $details['enrollments'] = $stmt->fetchAll();
   }

   return $details;
}
Depending on your needs though you could do anything from just change an array of fields to be selected to simply using two different querys.

$fields = array('a','b','c');
if ($loadEnrollments){ 
   $fields=array_merge($fields, array('d','e','f'));
}

//or
if ($loadEnrollments){
  $sql = 'The big query';
}
else {
  $sql = 'The smaller query';
}

$stmt = $db->query($sql);

 

If data transfer objects is what you are looking for. Doctrine has you covered aswell:

http://docs.doctrine-project.org/en/latest/reference/dql-doctrine-query-language.html#new-operator-syntax

Link to comment
Share on other sites

Try not to use transaction script unless you do not have a choice, instead apply the data mapper design pattern to make the best use of OOP. Transaction script is not even OOP to begin with, it is an anti-pattern with objects written in procedural code.

:facepalm:

 

If you don't take it from me, read this: http://stackoverflow.com/a/16400738

Edited by ignace
Link to comment
Share on other sites

In my particular case it just does a second query and then merges the results together in the code. Ie:

public static function GetDetails($id, $loadEnrollments=false){
   $stmt = /* query to get basic details */;
   $details = $stmt->fetchAll();
   if ($loadEnrollments){
       $stmt = /* query to get enrollment data */
       $details['enrollments'] = $stmt->fetchAll();
   }

   return $details;
}
Depending on your needs though you could do anything from just change an array of fields to be selected to simply using two different querys.
$fields = array('a','b','c');
if ($loadEnrollments){ 
   $fields=array_merge($fields, array('d','e','f'));
}

//or
if ($loadEnrollments){
  $sql = 'The big query';
}
else {
  $sql = 'The smaller query';
}

$stmt = $db->query($sql);

I'm just gonna use this since it makes most sense to me. I understand that using an ORM could be better but I simply don't have the time to learn that right now. It would take way too long to implement.

 

Thanks for the help guys

Link to comment
Share on other sites

You stare yourself blind on OOP. There is no such thing as a good OOP practice. There are only good practices and bad practices. When you have a small system it is a bad practice IMHO to create a very complex domain model for no other reason then "because it's true OO".

 

Remember this quote?

 

There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.

When developing software you have to consider the bigger picture and when a simpler method presents itself you do best to take it. Ask any industry expert, hell ask Martin Fowler himself, and they will tell you that when you can keep it simple, you keep it simple!

 

But I get it you are an eager student with no industry experience and you like OOP, as do I. But consider that at some occassions you just may be wrong. And if you can't/won't accept that, then the industry will teach you when you are fired because you made it so complex that even you can't figure it out anymore. But hey.. it's true OO, right?

Edited by ignace
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.