Zend framework - Why should I use data mapper / Db_Table_Row? - php

Extended Question: Why should I use data mapper / Db_Table_Row, where as DbTable is capable of handling most of the basic tasks for data manipulation.
I am currently learning ZF v1.11
For Database manipulation, I created DbTable for each tables. For example, "users" table is represented by Application_Model_DbTable_Users with no additional codes in there.
When manipulating data, I can use:
<?php
$uTable = new Application_Model_DbTable_Users();
$newUid = $uTable->insert(array('name'=>'Old Name', 'email'=>''));
$user = $uTable->find($newUid)->current();
// Then I can use $user which is instance of Table_Row
$user->name = "New Name";
$user->email = "email#addr.com";
$user->save();
My Question is, when would I need to define a row class (assuming Table_Row is referred as DataMapper in ZF-Tutorials)
// By, adding this to the DbTable class
protected $_rowClass = 'Application_Model_User';
What are the benefits of having a Row class for each entity? Can anyone point me to best practices for this.

You do not need to define your own Table_Row. However, it maybe useful in many cases, particularly if you want to define some specific methods or properties for a given user row. They can also improve readability of your code.
For example in your Users table case, you could define a method called getFullName() in a custom user row as:
public function getFullName() {
return $this->firstName . ' ' . $this->lastName;
}
Then when you obtain user row object, to get the full name of the user, you just do:
$user = $uTable->find($newUid)->current();
$fullName = $user->getFullName();
Second example is when you have some parent table to the Users table, such as Addresses. In this case you could define a method called getAddress in a user row:
public function getAddress() {
return $this->findParentRow('Application_Model_DbTable_Addresses');
}
In this scenario, you would get an Address row object for a current user as follows:
$user = $uTable->find($newUid)->current();
$addressRow = $user->getAddress();
Another example, would be when you want to create custom delete or instert methods. Lets assume that you want to make sure you do not want to delete an admin user using delete() method. Then you could overload delete method from Zend_Db_Table_Row as follows:
public function delete() {
if ('admin' === $this->userRole) {
return 0;
}
return parent::delete();
}
This way, you would not be able to delete an admin user just by calling delete() on a user row object:
$user = $uTable->find($newUid)->current();
$rowsDeleted = $user->delete(); // would be 0 if $user is admin
These are just three basic examples showing usefulness of defining your own row classes. But of course they are not necessary. However, from my own experience they are quite handy.

In a nutshell: it's about isolation.
Zend_Db_Table is an implementation of the Table Data Gateway. It channels CRUD access to a specific table view through one class. It is usually used with a Table Module, e.g. a class that contains the business logic for the records handled by a gateway.
Zend_Db_Table_Row is an implementation of the Row Data Gateway Pattern. Here, the returned objects look exactly like a database record and they contain the business logic to work with that data, but they don't contain the logic to CRUD with the table they are from (that would be an ActiveRecord) but aggregate them.
Row Data Gateways are fine as long as you don't have too much object relational impedance mismatch. How an object is persisted in a relational database and how it should look like in an object world are often quite different things. When using a Domain Model, your business objects are usually structured in a different way than they are stored in the database. Thus, you cannot easily CRUD them from the database. This is where DataMapper comes into play.
A DataMapper takes the responsibility of mapping Domain objects onto Recordsets and vice versa. This makes your application more maintainable because it decouples your Domain objects from the Database structure. It keeps them separated and gives you more flexibility in how to model both layers (persistence and domain).

Related

Design pattern to register handler (provider) to handle types of requests

I have scenario where I have collection of objects. Data of different objects are stored in different data sources. I am looking for design pattern to register data providers (Already have multiple data providers) for different objects. I need flexibility to add new type of objects and register handler to fetch data for those objects.
E.g. I need to display list of cities. I have city information in mysql city table. e.g. id, name, state, country. I need to display weather information as well which comes from 3rd party service. I also need to display some other information which is stored in mongo. e.g. population, literacy rate etc.
I need to fetch all above data from different data providers and display in tabular form. I need flexibility to add more type of such data.
There are several patterns which I think can be used to solve some part of problem e.g. command pattern, observer pattern, chain of responsibility, strategy etc.
I would like the code to be
$objects = [$cityMetadata, $weather, $demographic];
$dataProviderRegistry->register($cityMedatadaProvider);
$dataProviderRegistry->register($weatherDataProvider);
$dataProviderRegistry->register($demographicDataProvider);
foreach ($objects as $object) {
$data = $dataProviderRegistry->get($object);
}
I would like DataProviderRegistry to select correct provider for object registered to handle that type of object. I should be able to register new type of provider in registry to handle new types of objects.
I am not sure how one or more type of design patterns can be applied to solve this specific problem.
class WeatherDataProvider
{
public function getType()
{
return "Weather";
}
public function getData()
{
//fetch data
}
}
Class Weather
{
public function getType()
{
return "Weather";
}
}
class DataProviderRegistry
{
private $providers = [];
public function register(DataProviderInterface $provider)
{
$this->providers[$provider->getType()] = $provider;
}
public function get($object)
{
return $this->providers[$object->getType()]->getData();
}
}
I would just create a repository (or probably - multiple ones), that would mask the logic for choosing correct data mapper for each entity (business object object). You do not need to even implement the full repository. Just do the relevant part for you and leave out things like identity-map, if it is not currently needed.
You can even set it up so that, when entity is being saved or populated, the "repository" picks the matching data mapper. based on get_class($entity). That way you will be able simplify the external interface even further.
And in each data mapper you can put code for working with whatever persistence abstraction that you need to (sql, cache, rest, etc.).
TL;DR
Use the repository pattern as the basis and then mutate it according to your needs.

DataMapper Pattern with m:n relation (Domain Driven Design)

I am currently refactoring my software according to the Domain Driven Design. The problem is my understanding in the DataMapper part, specifically if some data have m:n relationship via a connection table (in case of SQL).
In this case I have a Domain Object:
class UserGroup extends DomainObject {
public $title;
public $description;
}
Now in my understanding, I would get an Object this way:
$userGroup = UserGroupMapper::instance()->findById(42);
$userGroup->title = 'Foo';
$userGroup->description = 'Bar';
UserGroupMapper::instance()->save($userGroup);
Am I on the right way, right now?
So here comes the problem:
I have 3 Tables:
core_users
id | etc
core_usergroups
id | title | description
core_usergroups_dependencys
usergroup_id | user_id
Basicly, the goal is, to find all Users which are in a specific group (or the inverted way)
The first thing, which comes to my mind:
$userGroup = UserGroupMapper::instance()->findById(42);
$userCollection = UserGroupMapper::instance()->findUsersByGroup($userGroup);
The DataMapper knows 2 tables and 2 Domain Objects, but this doesn't feel right, does it?
I don't understand the Service Part of DDD. According to it (in my understanding) I would write
UserGroupService::instance()->findUsers($userGroup);
But in this case the service need an own Data Mapper. Why shouldn't I call
UserGroupDependencyMapper::instance()->findUsersByGroup($userGroup);
UserGroupDependencyMapper::instance()->findGroupsByUser($user);
I generaly dont understand the mapping for dependency. A DataMapper should everytime return an object instance or a collection of object instances of the same type. But here, the mapper could return either a user or a group.
So, what is the way I should go for such a common problem? Bonus: What should my service do in this Example?
Update to Clarify
My question is not about working with the data in Domain Driven Design. It is about how can i fetch a list of data in m:n relation without fetching all. How should the Interface look like?
Basicly i need this according to the DataMapper pattern:
select * from core_users
join core_usergroups_dependency on core_users.id = core_usergroups_dependency.user_id
where core_usergroups_dependency.usergruop_id = 42
But where would the Call findUsersByUserGroup($myGroup) Call be located? UserMapper?
If i have that Mapper or how ever we will call this, which part knows about it? The Domain Repository?
The method is part of the repository interface definition (defined in the Domain). The implementation is part of the persistence and does the actual query. The Domain services will know only about the abstraction.
// in the Domain
interface IUsersRepository // I think we need a better name
{
public function getUsersByGroup($myGroup);
}
class MyDomainService
{
private $repo;
public function __construct(IUsersRepository $repo)
{
$this->repo = $repo;
}
public function doSomething($group)
{
$groups = $this->repo->getUsersByGroup($group);
// process $groups
}
}
// in DAL
class MyRepo implements IUsersRepository
{
// implementation
}
What matters here is that the Domain doesn't know about how you get those groups. Only the implementation knows that, so you can do whatever query you want there. The Domain is decoupled from the query itself.

Composition vs Inheritance. What should I use for my database interaction library?

Consider a Database interaction module written in PHP that contains classes for interacting with the database. I have not started coding the class so I won't be able to give code snippets.
There will be one class per database table as explained below.
User - A class for interacting with the user table. The class contains functions such as createUser, updateUser, etc.
Locations - A class for interacting with the locations table. The class contains functions such as searchLocation, createLocation, updateLocation, etc.
In addition, I am thinking of creating another class as follows: -
DatabaseHelper : A class that will have a member that represents the connection to the database. This class will contain the lower level methods for executing SQL queries such as executeQuery(query,parameters), executeUpdate(query,parameters) and so on.
At this point, I have two options to use the DatabaseHelper class in other classes : -
The User and Locations class will extend the DatabaseHelper class so that they can use the inherited executeQuery and executeUpdate methods in DatabaseHelper. In this case, DatabaseHelper will ensure that there is only one instance of the connection to the database at any given time.
The DatabaseHelper class will be injected in the User and Locations class through a Container class that will make User and Location instances. In this case, the Container will make sure that there is only one instance of DatabaseHelper in the application at any given time.
These are the two approaches that quickly come to my mind. I want to know which approach to go with. It is possible that both these approaches are not good enough, in which case, I want to know any other approach that I can go with to implement the database interaction module.
Edit:
Note that the Container class will contain a static member of type DatabaseHelper. It will contain a private static getDatabaseHelper() function that will return an existing DatabaseHelper instance or create a new DatabaseHelper instance if one does not exists in which case, it will populate the connection object in DatabaseHelper. The Container will also contain static methods called makeUser and makeLocation that will inject the DatabaseHelper into User and Locations respectively.
After reading a few answers, I realize that the initial question has almost been answered. But there is still a doubt that needs to be clarified before I can accept the final answer which is as follows.
What to do when I have multiple databases to connect to rather than a single database. How does the DatabaseHelper class incorporate this and how does the container inject appropriate database dependencies in the User and Location objects?
Lets answer your questions from top to bottom, and see what I can add to what you say.
There will be one class per database table as explained below.
User - A class for interacting with the user table. The class contains functions such as createUser, updateUser, etc.
Locations - A class for interacting with the locations table. The class contains functions >such as searchLocation, createLocation, updateLocation, etc.
Essentially you have to choices here. The method you described is called the active record pattern. The object itself knows how and where it is stored. For simple objects that interact with a database to create / read / update / delete, this pattern is really usefull.
If the database operations become more extensive and less simple to understand, it is often a good choice to go with a data mapper (eg. this implementation). This is a second object that handles all the database interactions, while the object itself (eg. User or Location) only handles operations that are specific to that object (eg. login or goToLocation). If you ever want to chance the storage of your objects, you will only have to create a new data mapper. Your object won't even know that something changed in the implementation. This enforces encapsulation and seperation of concerns.
There are other options, but these two are the most used ways to implement database interactions.
In addition, I am thinking of creating another class as follows: -
DatabaseHelper : A class that will have a static member that represents the connection to the database. This class will contain the lower level methods for executing SQL queries such as executeQuery(query,parameters), executeUpdate(query,parameters) and so on.
What you are describing here sounds like a singleton. Normally this isn't really a good design choice. Are you really, really certain that there will never be a second database? Probably not, so you should not confine yourself to an implementation that only allowes for one database connection. Instead of making a DatabaseHelper with static members, you can better create a Database object with some methods that allow you to connect, disconnect, execute a query, etc. This way you can reuse it if you ever need a second connection.
At this point, I have two options to use the DatabaseHelper class in other classes : -
The User and Locations class will extend the DatabaseHelper class so that they can use the inherited executeQuery and executeUpdate methods in DatabaseHelper. In this case, DatabaseHelper will ensure that there is only one instance of the connection to the database at any given time.
The DatabaseHelper class will be injected in the User and Locations class through a Container class that will make User and Location instances. In this case, the Container will make sure that there is only one instance of DatabaseHelper in the application at any given time.
These are the two approaches that quickly come to my mind. I want to know which approach to go with. It is possible that both these approaches are not good enough, in which case, I want to know any other approach that I can go with to implement the database interaction module.
The first option isn't really viable. If you read the description of inheritance, you will see that inheritance is normally used to create a subtype of an existing object. An User is not a subtype of a DatabaseHelper, nor is a location. A MysqlDatabase would be a subtype of a Database, or a Admin would be a subtype of an User. I would advise against this option, as it isn't following the best practices of object oriented programming.
The second option is better. If you choose to use the active record method, you should indeed inject the Database into the User and Location objects. This should of course be done by some third object that handles all these kind of interactions. You will probably want to take a look at dependency injection and inversion of control.
Otherwise, if you choose the data mapper method, you should inject the Database into the data mapper. This way it is still possible to use several databases, while seperating all your concerns.
For more information about the active record pattern and the data mapper pattern, I would advise you to get the Patterns of Enterprise Application Architecture book of Martin Fowler. It is full of these kind of patterns and much, much more!
I hope this helps (and sorry if there are some really bad English sentences in there, I'm not a native speaker!).
== EDIT ==
Using the active record pattern of data mapper pattern also helps in testing your code (like Aurel said). If you seperate all peaces of code to do just one thing, it will be easier to check that it is really doing this one thing. By using PHPUnit (or some other testing framework) to check that your code is properly working, you can be pretty sure that no bugs will be present in each of your code units. If you mix up the concerns (like when you choose option 1 of your choices), this will be a whole lot harder. Things get pretty mixed up, and you will soon get a big bunch of spaghetti code.
== EDIT2 ==
An example of the active record pattern (that is pretty lazy, and not really active):
class Controller {
public function main() {
$database = new Database('host', 'username', 'password');
$database->selectDatabase('database');
$user = new User($database);
$user->name = 'Test';
$user->insert();
$otherUser = new User($database, 5);
$otherUser->delete();
}
}
class Database {
protected $connection = null;
public function __construct($host, $username, $password) {
// Connect to database and set $this->connection
}
public function selectDatabase($database) {
// Set the database on the current connection
}
public function execute($query) {
// Execute the given query
}
}
class User {
protected $database = null;
protected $id = 0;
protected $name = '';
// Add database on creation and get the user with the given id
public function __construct($database, $id = 0) {
$this->database = $database;
if ($id != 0) {
$this->load($id);
}
}
// Get the user with the given ID
public function load($id) {
$sql = 'SELECT * FROM users WHERE id = ' . $this->database->escape($id);
$result = $this->database->execute($sql);
$this->id = $result['id'];
$this->name = $result['name'];
}
// Insert this user into the database
public function insert() {
$sql = 'INSERT INTO users (name) VALUES ("' . $this->database->escape($this->name) . '")';
$this->database->execute($sql);
}
// Update this user
public function update() {
$sql = 'UPDATE users SET name = "' . $this->database->escape($this->name) . '" WHERE id = ' . $this->database->escape($this->id);
$this->database->execute($sql);
}
// Delete this user
public function delete() {
$sql = 'DELETE FROM users WHERE id = ' . $this->database->escape($this->id);
$this->database->execute($sql);
}
// Other method of this user
public function login() {}
public function logout() {}
}
And an example of the data mapper pattern:
class Controller {
public function main() {
$database = new Database('host', 'username', 'password');
$database->selectDatabase('database');
$userMapper = new UserMapper($database);
$user = $userMapper->get(0);
$user->name = 'Test';
$userMapper->insert($user);
$otherUser = UserMapper(5);
$userMapper->delete($otherUser);
}
}
class Database {
protected $connection = null;
public function __construct($host, $username, $password) {
// Connect to database and set $this->connection
}
public function selectDatabase($database) {
// Set the database on the current connection
}
public function execute($query) {
// Execute the given query
}
}
class UserMapper {
protected $database = null;
// Add database on creation
public function __construct($database) {
$this->database = $database;
}
// Get the user with the given ID
public function get($id) {
$user = new User();
if ($id != 0) {
$sql = 'SELECT * FROM users WHERE id = ' . $this->database->escape($id);
$result = $this->database->execute($sql);
$user->id = $result['id'];
$user->name = $result['name'];
}
return $user;
}
// Insert the given user
public function insert($user) {
$sql = 'INSERT INTO users (name) VALUES ("' . $this->database->escape($user->name) . '")';
$this->database->execute($sql);
}
// Update the given user
public function update($user) {
$sql = 'UPDATE users SET name = "' . $this->database->escape($user->name) . '" WHERE id = ' . $this->database->escape($user->id);
$this->database->execute($sql);
}
// Delete the given user
public function delete($user) {
$sql = 'DELETE FROM users WHERE id = ' . $this->database->escape($user->id);
$this->database->execute($sql);
}
}
class User {
public $id = 0;
public $name = '';
// Other method of this user
public function login() {}
public function logout() {}
}
== EDIT 3: after edit by bot ==
Note that the Container class will contain a static member of type DatabaseHelper. It will contain a private static getDatabaseHelper() function that will return an existing DatabaseHelper instance or create a new DatabaseHelper instance if one does not exists in which case, it will populate the connection object in DatabaseHelper. The Container will also contain static methods called makeUser and makeLocation that will inject the DatabaseHelper into User and Locations respectively.
After reading a few answers, I realize that the initial question has almost been answered. But there is still a doubt that needs to be clarified before I can accept the final answer which is as follows.
What to do when I have multiple databases to connect to rather than a single database. How does the DatabaseHelper class incorporate this and how does the container inject appropriate database dependencies in the User and Location objects?
I think there is no need for any static property, nor does the Container need those makeUser of makeLocation methods. Lets assume that you have some entry point of your application, in which you create a class that will control all flow in your application. You seem to call it a container, I prefer to call it a controller. After all, it controls what happens in your application.
$controller = new Controller();
The controller will have to know what database it has to load, and if there is one single database or multiple ones. For example, one database contains the user data, anonther database contains the location data. If the active record User from above and a similar Location class are given, then the controller might look as follows:
class Controller {
protected $databases = array();
public function __construct() {
$this->database['first_db'] = new Database('first_host', 'first_username', 'first_password');
$this->database['first_db']->selectDatabase('first_database');
$this->database['second_db'] = new Database('second_host', 'second_username', 'second_password');
$this->database['second_db']->selectDatabase('second_database');
}
public function showUserAndLocation() {
$user = new User($this->databases['first_database'], 3);
$location = $user->getLocation($this->databases['second_database']);
echo 'User ' . $user->name . ' is at location ' . $location->name;
}
public function showLocation() {
$location = new Location($this->database['second_database'], 5);
echo 'The location ' . $location->name . ' is ' . $location->description;
}
}
Probably it would be good to move all the echo's to a View class or something. If you have multiple controller classes, it might pay off to have a different entrypoint that creates all databases and pushes them in the controller. You could for example call this a front controller or an entry controller.
Does this answer you open questions?
I would go with the dependancy injection, for the following reason: if at some point you want to write tests for your applications, it will allow you to replace the DatabaseHelper instance by a stub class, implementing the same interface but that do not really access a database. This will make it really easier to test your model functionalities.
By the way, for this to be really useful, your other classes (User, Locations) should depend on a DatabaseHelperInterface rather than directly on DatabaseHelper. (This is required to be able to switch implementations)
The question of Dependency Injection vs. Inheritance, at least in your specific example comes down to the following: "is a" or "has a".
Is class foo a type of class bar? Is it a bar? If so, maybe inheritance is the way to go.
Does class foo use an object of class bar? You're now in dependency injection territory.
In your case, your data access objects (in my code approach these are UserDAO and LocationDAO) are NOT types of database helpers. You would not use a UserDAO, for instance, to provide database access to another DAO class. Instead, you USE the features of a database helper in your DAO classes. Now, this doesn't mean that technically you could not achieve what you want to do by extending the database helper classes. But I think it would be a bad design, and would cause trouble down the road as your design evolves.
Another way to think about it is, is ALL of your data going to come from the database? What if, somewhere down the road, you want to pull some location data from, say, an RSS feed. You have your LocationDAO essentially defining your interface -- your "contract", so to speak -- as to how the rest of your application obtains location data. But if you had extended DatabaseHelper to implement your LocationDAO, you'd now be stuck. There'd be no way to have your LocationDAO use a different data source. If, however, DatabaseHelper and your RSSHelper both had a common interface, you could plug the RSSHelper right into your DAO and LocationDAO doesn't even have to change at all. *
If you had made LocationDAO a type of DatabaseHandler, changing the data source would require changing the type of LocationDAO. This means that not only does LocationDAO have to change, but all your code that uses LocationDAO has to change. If you had injected a datasource into your DAO classes from the start, then the LocationDAO interface would remain the same, regardless of the datasource.
(* Just a theoretical example. There'd be a lot more work to get a DatabaseHelper and RSSHelper to have a similar interface.)
What you are describing with your User and Location classes is called a Table Data Gateway:
An object that acts as a Gateway to a database table. One instance handles all the rows in the table.
In general, you want to favor Composition over Inheritance and programm towards an interface. While it may seem like more effort to assemble your objects, doing it will benefit maintenance and the ability to change the program in the long run (and we all know change is the only ever constant in a project).
The most obvious benefit of using Dependency Injection here is when you want to unit test the Gateways. You cannot easily mock away the connection to the database when using inheritance. This means you will always have to have a database connection for these tests. Using Depedency Injection allows you to mock that connection and just test the Gateways interact correctly with the Database Helper.
Even though the other answers here are very good, I wanted to throw in some other thoughts from my experiences using CakePHP (an MVC framework). Basically, I will just show you a leaf or two out of their API; mainly because - to me - it seems well defined and thought out (probably because I use it daily).
class DATABASE_CONFIG { // define various database connection details here (default/test/externalapi/etc) }
// Data access layer
class DataSource extends Object { // base for all places where data comes from (DB/CSV/SOAP/etc) }
// - Database
class DboSource extends DataSource { // base for all DB-specific datasources (find/count/query/etc) }
class Mysql extends DboSource { // MySQL DB-specific datasource }
// - Web service
class SoapSource extends DataSource { // web services, etc don't extend DboSource }
class AcmeApi extends SoapSource { // some non-standard SOAP API to wrestle with, etc }
// Business logic layer
class Model extends Object { // inject a datasource (definitions are in DATABASE_CONFIG) }
// - Your models
class User extends Model { // createUser, updateUser (can influence datasource injected above) }
class Location extends Model { // searchLocation, createLocation, updateLocation (same as above) }
// Flow control layer
class Controller extends Object { // web browser controls: render view, redirect, error404, etc }
// - Your controllers
class UsersController extends Controller { // inject the User model here, implement CRUD, this is where your URLs map to (eg. /users/view/123) }
class LocationsController extends Controller { // more CRUD, eg. $this->Location->search() }
// Presentation layer
class View extends Object { // load php template, insert data, wrap in design }
// - Non-HTML output
class XmlView extends View { // expose data as XML }
class JsonView extends View { // expose data as JSON }
Dependency Injection is preferred if you have different types of services, and one service want to use other.
Your classes User and Locations sounds more like DAO (DataAccessObject) layer, that interact with database, So for your given case you should be using In Inheritance. Inheritance can be done by extending class or implementing Interfaces
public interface DatabaseHelperInterface {
public executeQuery(....);
}
public class DatabaseHelperImpl implemnets DatabaseHelperInterface {
public executeQuery(....) {
//some code
}
public Class UserDaoInterface extends DatabaseHelperInterface {
public createUser(....);
}
public Class UserDaoImpl extends DatabaseHelperImpl {
public createUser(....) {
executeQuery(create user query);
}
In this way your database design and code will be separate.

Is it okay for a model to populate its own properties?

If I have a model that essentially represents a single row in a database, is it scale-friendly and test-friendly, and all around okay practice to have it populate it's own properties, or should it have its properties injected, the same way you would inject an object?
Example:
Class blog {
$id;
$title;
$body;
public function load($id) {
// db query to load id, title, body
}
}
OR
Class blog {
$id
$title
$body
}
// load blog data into $data, and then...
$blog = new Blog($data)
It's considered a bad practice to commingle database access inside your model classes directly. Injecting values is generally preferred.
That said, if you were dead set on doing something like $model->load($id) and having it fetch from a datasource, you could get away with something like:
class Model {
private $_dataProvider;
// inject data-provider dependency in constructor
public function __construct($dataProvider){
$this->_dataProvider = $dataProvider;
}
public function loadById($id){
$myData = $this->_dataProvider->loadDataById($id);
$this->setFoo($myData['foo']);
...
}
}
By injecting a data access class, you can pass a mock in for testing, or replace your database with some web service, or whatever. As long as $dataProvider has a loadDataById() method that takes an int and returns the appropriate data structure, you're good.
Personally, I prefer keep my models nice and focused on representing whatever it is they're modeling. I rely on external service classes and repositories to load data, inject it into models, and return them.
If you want to de-couple the data storage layer from the models itself, you should make it injectable.
If the models are the data storage abstraction, you don't need to care, you only need to inject the models which then should have defined interfaces so you have the rest of your application testable.
But it merely depends on your needs and your design.

Design pattern for users

I'm starting to code my project and I have used, in some past unfinished project, two different patterns to manage the design of a user class that will need to manage the following things:
The creation of a user
The edits to a user
The deletion of a user
The reading of user data
Despite this, we have also to consider that this user class will be extended by the session class which will just set the focused user id as the id provided by the user who is viewing the pages.
We will also have this class users that will manage instead groups of users.
The 2 options I used are the following (simplified):
Different class for different purpose
- class UserMaker($username, $password, $email);
function giveBirth(); // create the user
- class UserManager($id);
function edit($field, $value); // edit a specific user field
function save(); // save all the edits with a single query
function setTrusted(); // set that user as trusted
function setAdmin(); // set that user as admin
function setBanned(); // ban the specific user
- class UserReader($id);
function get($field); // Get the value of a single field
function getAll(); // Get all fields from that user as associative array
function isAdmin(); // self explanation
function isRegistered(); // self explanation
function isBanned(); // self explanation
Single class
- class User($id);
function static giveBirth($username, $password, $email); // create the user, notice this is static
function edit($field, $value); // edit a specific user field
function save(); // save all the edits with a single query
function setTrusted(); // set that user as trusted
function setAdmin(); // set that user as admin
function setBanned(); // ban the specific user
function get($field); // Get the value of a single field
function getAll(); // Get all fields from that user as associative array
function isAdmin(); // self explanation
function isRegistered(); // self explanation
function isBanned(); // self explanation
Basically, since the only class that does not accept $id as argument for the __construct() is UserMaker we just set the function giveBirth() as static so we can create the user.
What is the best way to design this pattern? Have you got a third-option which you feel better than these?
Well, the answer to this question relates specifically with The Single Responsibility Principle. Basically, each and every class in your application should have exactly one responsibility. That doesn't mean that it can't be used in more than one situation, but it shouldn't be responsible for more than one abstract concept.
So, from your examples, I would build a class structure similar to this:
class UserFactory()
getModel();
getUser($id, $model = null);
getACL($user);
class UserModel ()
edit($id = 0);
load($id = 0);
reload($id = 0);
save($id = 0);
class User ($data)
getAll();
getField($field);
class UserACL (User $user)
isTrustedUser();
isAdminUser();
isBannedUser();
That way, everything is organized by responsibility and role, rather than by relation. The benefit to this, is that if you want to swap out an individual component later (for example, the ACL system, or the Model storage layer), you don't need to worry about testing a huge class. Just test that particular API and you're done.
I do not know how exactly have you organized/conceived stuff in your system although from the approaches that I have used, I've found out that the most efficient way of handling stuff is by conceiving objects as services for example:
How many times in your software will the giveBirth method be called? once, twice more ? why more?
Although let's say that your user object must expose methods which can not be called in an exact place every time or you want it to have a more general effect.
Create an interface(or interfaces) with the methods headers.
Create a SINGLETON that implements the interface (all the interfaces). That is because I assume you will never have more then an instance of an user during one request.(fact that is also available for the session).
in the singleton you will have an init static method which can receive an id in order to create the user instance. You will have something like this in the index as I do assume there is a single entry to your application
UserSingleton::init($_SESSION["id"]);
after the request process initializes the user object you can use this :
$user = UserSingleton::getInstance(); // will return the instance of singleton
$user->setBanned();// or whatever you need to call;
Obs:
you can avoid the init method by creating the getInstanceMethod like this:
public function getInstance(){
if(!isset(self::$instance) {
self::$instance = new UserSingleton($_SESSION["user_id"]) ;
}
return self::$instance;
}
Take note that this must not handle stuff like interaction with the database, this should receive/send data to/from the database through external stuff. It is a helper not a business model/object. That is another concern.

Categories