User Action - singleton

Hi,

I’ve been thinking that I would like to decouple the User object from any actions that they are allowed to do. Because I don’t know how many actions a user will do, I was thinking about implementing a singleton class.

A quick example:


<?php
class User {
	private $id;
	private $firstname;
	private $lastname;
	private $email;
	
	public function __construct($id, $firstname, $lastname) {
		$this->id = $id;
		$this->firstname = $firstname;
		$this->lastname = $lastname;
	}
}

class UserAction {
	private static $instance = null;
	private $user;
	
	private function __construct(User $user) {
		$this->user = $user;
	}
	
	public static function getInstance(User $user) {
		if ($instance == null) {
			return self::$instance = new UserAction($user);
		} else {
			return self::$instance;
		}
	}
	
	public function emailBuddy($userId) {
		//etc..
	}
	
	public function updateAccount() {
		//etc..
	}
	
	public function postNews() {
		//etc..
	}

    //many many more action methods....
}
?>

As you can see, multiple actions could occur. If I were not to use a singleton than a new UserAction object would have to be created every time an action occurs.

Best case scenario would be that a user only did one action, no exceptions were thrown / if statement fails (cause the application to change behavior) and no other class called a user action method.

This isn’t an actual project, just something I thought about. I would like to see how a non singleton approach would be taken. I also remember someone saying something about how singleton classes cannot be tested? Or something around that nature.

Thanks

I think that Factory should create and return instances, method Login belongs to another class.

Factory extending DTO is another lapse, IMHO. And DTO (if it is really Data transfer object) should not inherit connections. DTO should be a serializable value box.

Passing the DAO class to the factory through the constructor is good idea.

You seem to imply that you think that would be a problem. What makes you think that?

Usually the UserObject should be a singleton.

The objects that take objUser as a parameter don’t really have to be singletons.

It’s up to you how you implement singleton pattern, the getInstance() will work, there are also registry and dependency injection patterns that can produce a single instance of object.

Hi…

Why?

yours, Marcus

Because if it’s not a singleton then potentially 2 different objects may be created during the execution of your program that represent the same user. Then changes to one object are not reflecting the second object. By singleton I mean a single instance of the user object that represent the same user. Of cause there could be many user objects, each representing different user.

If only one user is allowed, then how can user A create a new user, user B?

I think you’re confusing User with Authentication System …

I am not sure what you mean by user A create user B?
Why one user object need to create another user object?
Maybe we are talking about different scenarios. I was just talking generally that user objects in general should follow singleton pattern, meaning only one user object allowed to represent the same user. Different users of cause should have different objects.

Ah, that wasn’t clear in your previous post. We’re on the same page now :slight_smile:

Well a User object can be created many different ways. A few are:

-Login
-A collection of User objects
-By Id
etc…

For that though, I use a factory.

Which leads me to my next question.

I was thinking about passing a DTO to the factory through the constructor to allow the creation of users. Currently, the factory extends a DTO class which inherits all connections (I use MySQL and MongoDB). Through the factory I just do something like this:


class UserFactory extends ApplicationDTO {
  //...
  public function login($email, $password) {
    $mysql = $this->get('mysql'); // I can now use PDO to access a database
    //or maybe I want to use mongo db?
    $mongo = $this->get('mongo');

    //use $mysql / $mongo to execute a query...
    //etc ...
  }
  //...
}

The only thing I don’t like about the approach is that I have sql all over the factory. I was thinking about abstracting that and passing the class to the factory through the constructor.

What do you guys think?

Sorry, I get the DTO / DAO confused.

The factory is a good place I think. Consider this:


class UserFactory {

	public function login($email, $password) {
		$stmt = $this->dao->login($email, $password);

		if ($stmt != null) {
			return $this->create($stmt);
		}
		throw new Exception('Incorrect credentials');
	}
	
	public function byId($userId) {
		$stmt = $this->dao->byId($userId);

		if ($stmt != null) {
			return $this->create($stmt);
		}
		throw new Exception('User ID not found');
	}
	
	private function create(PDOStatement $stmt) {
		$user = $stmt->fetch(PDO::FETCH_ASSOC);
	
		return new User($user['fname'], $user['lname'], $user['email']);
	}
}

class LogIn extends ApplicationController {
	
	public function defaultAction() {
		$request = $this->getRequest();
		
		if ($request->isPost()) {
			$email = $request->param('email');
			$password = $request->param('password');
			$uf = new UserFactory();
			
			try {
				$user = $uf->login($email, $password);
				
				//etc....
			} catch(Exception $e) {
				// Unable to login
			}
		}
	}
}

Although right now I don’t have it implemented this way. The SQL is mixed in with the factory but the logic is the same.

What do you think?

It seems like you would like to be able to change the DB, while adhering to the same interface. In that case it only makes sense to actually use some type interface that hosts all the user specific business transactions. This will allow the two to be interchanged, but define the details of getting the data differently. Others will also be able to be easily added to the work flow if necessary, without changing any application level, controller code.


interface IUserDAO {
	public function login($email,$password);
	public function byId($userId);
}

class MongoUserDAO implements IUserDAO {
	public function login($email,$password) {
		/* mongo specific login implementation */
	}
	public function byId($userId) {
		/* mongo specific fetch user */
	}
}

class MySQLUserDAO implements IUserDAO {
	public function login($email,$password) {
		/* mysql specific login implementation */
	}
	public function byId($userId) {
		/* mysql specific fetch user */
	}
}