Preface: I'm attempting to use the repository pattern in an MVC architecture with relational databases.
I've recently started learning TDD in PHP, and I'm realizing that my database is coupled much too closely with the rest of my application. I've read about repositories and using an IoC container to "inject" it into my controllers. Very cool stuff. But now have some practical questions about repository design. Consider the follow example.
<?php
class DbUserRepository implements UserRepositoryInterface
{
protected $db;
public function __construct($db)
{
$this->db = $db;
}
public function findAll()
{
}
public function findById($id)
{
}
public function findByName($name)
{
}
public function create($user)
{
}
public function remove($user)
{
}
public function update($user)
{
}
}
Issue #1: Too many fields
All of these find methods use a select all fields (SELECT *) approach. However, in my apps, I'm always trying to limit the number of fields I get, as this often adds overhead and slows things down. For those using this pattern, how do you deal with this?
Issue #2: Too many methods
While this class looks nice right now, I know that in a real-world app I need a lot more methods. For example:
findAllByNameAndStatus
findAllInCountry
findAllWithEmailAddressSet
findAllByAgeAndGender
findAllByAgeAndGenderOrderByAge
Etc.
As you can see, there could be a very, very long list of possible methods. And then if you add in the field selection issue above, the problem worsens. In the past I'd normally just put all this logic right in my controller:
<?php
class MyController
{
public function users()
{
$users = User::select('name, email, status')
->byCountry('Canada')->orderBy('name')->rows();
return View::make('users', array('users' => $users));
}
}
With my repository approach, I don't want to end up with this:
<?php
class MyController
{
public function users()
{
$users = $this->repo->get_first_name_last_name_email_username_status_by_country_order_by_name('Canada');
return View::make('users', array('users' => $users))
}
}
Issue #3: Impossible to match an interface
I see the benefit in using interfaces for repositories, so I can swap out my implementation (for testing purposes or other). My understanding of interfaces is that they define a contract that an implementation must follow. This is great until you start adding additional methods to your repositories like findAllInCountry(). Now I need to update my interface to also have this method, otherwise, other implementations may not have it, and that could break my application. By this feels insane...a case of the tail wagging the dog.
Specification Pattern?
This leads me to believe that repository should only have a fixed number of methods (like save(), remove(), find(), findAll(), etc). But then how do I run specific lookups? I've heard of the Specification Pattern, but it seems to me that this only reduces an entire set of records (via IsSatisfiedBy()), which clearly has major performance issues if you're pulling from a database.
Help?
Clearly, I need to rethink things a little when working with repositories. Can anyone enlighten on how this is best handled?
I thought I'd take a crack at answering my own question. What follows is just one way of solving the issues 1-3 in my original question.
Disclaimer: I may not always use the right terms when describing patterns or techniques. Sorry for that.
The Goals:
Create a complete example of a basic controller for viewing and editing Users.
All code must be fully testable and mockable.
The controller should have no idea where the data is stored (meaning it can be changed).
Example to show a SQL implementation (most common).
For maximum performance, controllers should only receive the data they need—no extra fields.
Implementation should leverage some type of data mapper for ease of development.
Implementation should have the ability to perform complex data lookups.
The Solution
I'm splitting my persistent storage (database) interaction into two categories: R (Read) and CUD (Create, Update, Delete). My experience has been that reads are really what causes an application to slow down. And while data manipulation (CUD) is actually slower, it happens much less frequently, and is therefore much less of a concern.
CUD (Create, Update, Delete) is easy. This will involve working with actual models, which are then passed to my Repositories for persistence. Note, my repositories will still provide a Read method, but simply for object creation, not display. More on that later.
R (Read) is not so easy. No models here, just value objects. Use arrays if you prefer. These objects may represent a single model or a blend of many models, anything really. These are not very interesting on their own, but how they are generated is. I'm using what I'm calling Query Objects.
The Code:
User Model
Let's start simple with our basic user model. Note that there is no ORM extending or database stuff at all. Just pure model glory. Add your getters, setters, validation, whatever.
class User
{
public $id;
public $first_name;
public $last_name;
public $gender;
public $email;
public $password;
}
Repository Interface
Before I create my user repository, I want to create my repository interface. This will define the "contract" that repositories must follow in order to be used by my controller. Remember, my controller will not know where the data is actually stored.
Note that my repositories will only every contain these three methods. The save() method is responsible for both creating and updating users, simply depending on whether or not the user object has an id set.
interface UserRepositoryInterface
{
public function find($id);
public function save(User $user);
public function remove(User $user);
}
SQL Repository Implementation
Now to create my implementation of the interface. As mentioned, my example was going to be with an SQL database. Note the use of a data mapper to prevent having to write repetitive SQL queries.
class SQLUserRepository implements UserRepositoryInterface
{
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function find($id)
{
// Find a record with the id = $id
// from the 'users' table
// and return it as a User object
return $this->db->find($id, 'users', 'User');
}
public function save(User $user)
{
// Insert or update the $user
// in the 'users' table
$this->db->save($user, 'users');
}
public function remove(User $user)
{
// Remove the $user
// from the 'users' table
$this->db->remove($user, 'users');
}
}
Query Object Interface
Now with CUD (Create, Update, Delete) taken care of by our repository, we can focus on the R (Read). Query objects are simply an encapsulation of some type of data lookup logic. They are not query builders. By abstracting it like our repository we can change it's implementation and test it easier. An example of a Query Object might be an AllUsersQuery or AllActiveUsersQuery, or even MostCommonUserFirstNames.
You may be thinking "can't I just create methods in my repositories for those queries?" Yes, but here is why I'm not doing this:
My repositories are meant for working with model objects. In a real world app, why would I ever need to get the password field if I'm looking to list all my users?
Repositories are often model specific, yet queries often involve more than one model. So what repository do you put your method in?
This keeps my repositories very simple—not an bloated class of methods.
All queries are now organized into their own classes.
Really, at this point, repositories exist simply to abstract my database layer.
For my example I'll create a query object to lookup "AllUsers". Here is the interface:
interface AllUsersQueryInterface
{
public function fetch($fields);
}
Query Object Implementation
This is where we can use a data mapper again to help speed up development. Notice that I am allowing one tweak to the returned dataset—the fields. This is about as far as I want to go with manipulating the performed query. Remember, my query objects are not query builders. They simply perform a specific query. However, since I know that I'll probably be using this one a lot, in a number of different situations, I'm giving myself the ability to specify the fields. I never want to return fields I don't need!
class AllUsersQuery implements AllUsersQueryInterface
{
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function fetch($fields)
{
return $this->db->select($fields)->from('users')->orderBy('last_name, first_name')->rows();
}
}
Before moving on to the controller, I want to show another example to illustrate how powerful this is. Maybe I have a reporting engine and need to create a report for AllOverdueAccounts. This could be tricky with my data mapper, and I may want to write some actual SQL in this situation. No problem, here is what this query object could look like:
class AllOverdueAccountsQuery implements AllOverdueAccountsQueryInterface
{
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function fetch()
{
return $this->db->query($this->sql())->rows();
}
public function sql()
{
return "SELECT...";
}
}
This nicely keeps all my logic for this report in one class, and it's easy to test. I can mock it to my hearts content, or even use a different implementation entirely.
The Controller
Now the fun part—bringing all the pieces together. Note that I am using dependency injection. Typically dependencies are injected into the constructor, but I actually prefer to inject them right into my controller methods (routes). This minimizes the controller's object graph, and I actually find it more legible. Note, if you don't like this approach, just use the traditional constructor method.
class UsersController
{
public function index(AllUsersQueryInterface $query)
{
// Fetch user data
$users = $query->fetch(['first_name', 'last_name', 'email']);
// Return view
return Response::view('all_users.php', ['users' => $users]);
}
public function add()
{
return Response::view('add_user.php');
}
public function insert(UserRepositoryInterface $repository)
{
// Create new user model
$user = new User;
$user->first_name = $_POST['first_name'];
$user->last_name = $_POST['last_name'];
$user->gender = $_POST['gender'];
$user->email = $_POST['email'];
// Save the new user
$repository->save($user);
// Return the id
return Response::json(['id' => $user->id]);
}
public function view(SpecificUserQueryInterface $query, $id)
{
// Load user data
if (!$user = $query->fetch($id, ['first_name', 'last_name', 'gender', 'email'])) {
return Response::notFound();
}
// Return view
return Response::view('view_user.php', ['user' => $user]);
}
public function edit(SpecificUserQueryInterface $query, $id)
{
// Load user data
if (!$user = $query->fetch($id, ['first_name', 'last_name', 'gender', 'email'])) {
return Response::notFound();
}
// Return view
return Response::view('edit_user.php', ['user' => $user]);
}
public function update(UserRepositoryInterface $repository)
{
// Load user model
if (!$user = $repository->find($id)) {
return Response::notFound();
}
// Update the user
$user->first_name = $_POST['first_name'];
$user->last_name = $_POST['last_name'];
$user->gender = $_POST['gender'];
$user->email = $_POST['email'];
// Save the user
$repository->save($user);
// Return success
return true;
}
public function delete(UserRepositoryInterface $repository)
{
// Load user model
if (!$user = $repository->find($id)) {
return Response::notFound();
}
// Delete the user
$repository->delete($user);
// Return success
return true;
}
}
Final Thoughts:
The important things to note here are that when I'm modifying (creating, updating or deleting) entities, I'm working with real model objects, and performing the persistance through my repositories.
However, when I'm displaying (selecting data and sending it to the views) I'm not working with model objects, but rather plain old value objects. I only select the fields I need, and it's designed so I can maximum my data lookup performance.
My repositories stay very clean, and instead this "mess" is organized into my model queries.
I use a data mapper to help with development, as it's just ridiculous to write repetitive SQL for common tasks. However, you absolutely can write SQL where needed (complicated queries, reporting, etc.). And when you do, it's nicely tucked away into a properly named class.
I'd love to hear your take on my approach!
July 2015 Update:
I've been asked in the comments where I ended up with all this. Well, not that far off actually. Truthfully, I still don't really like repositories. I find them overkill for basic lookups (especially if you're already using an ORM), and messy when working with more complicated queries.
I generally work with an ActiveRecord style ORM, so most often I'll just reference those models directly throughout my application. However, in situations where I have more complex queries, I'll use query objects to make these more reusable. I should also note that I always inject my models into my methods, making them easier to mock in my tests.
Based on my experience, here are some answers to your questions:
Q: How do we deal with bringing back fields we don't need?
A: From my experience this really boils down to dealing with complete entities versus ad-hoc queries.
A complete entity is something like a User object. It has properties and methods, etc. It's a first class citizen in your codebase.
An ad-hoc query returns some data, but we don't know anything beyond that. As the data gets passed around the application, it is done so without context. Is it a User? A User with some Order information attached? We don't really know.
I prefer working with full entities.
You are right that you will often bring back data you won't use, but you can address this in various ways:
Aggressively cache the entities so you only pay the read price once from the database.
Spend more time modeling your entities so they have good distinctions between them. (Consider splitting a large entity into two smaller entities, etc.)
Consider having multiple versions of entities. You can have a User for the back end and maybe a UserSmall for AJAX calls. One might have 10 properties and one has 3 properties.
The downsides of working with ad-hoc queries:
You end up with essentially the same data across many queries. For example, with a User, you'll end up writing essentially the same select * for many calls. One call will get 8 of 10 fields, one will get 5 of 10, one will get 7 of 10. Why not replace all with one call that gets 10 out of 10? The reason this is bad is that it is murder to re-factor/test/mock.
It becomes very hard to reason at a high level about your code over time. Instead of statements like "Why is the User so slow?" you end up tracking down one-off queries and so bug fixes tend to be small and localized.
It's really hard to replace the underlying technology. If you store everything in MySQL now and want to move to MongoDB, it's a lot harder to replace 100 ad-hoc calls than it is a handful of entities.
Q: I will have too many methods in my repository.
A: I haven't really seen any way around this other than consolidating calls. The method calls in your repository really map to features in your application. The more features, the more data specific calls. You can push back on features and try to merge similar calls into one.
The complexity at the end of the day has to exist somewhere. With a repository pattern we've pushed it into the repository interface instead of maybe making a bunch of stored procedures.
Sometimes I have to tell myself, "Well it had to give somewhere! There are no silver bullets."
I use the following interfaces:
Repository - loads, inserts, updates and deletes entities
Selector - finds entities based on filters, in a repository
Filter - encapsulates the filtering logic
My Repository is database agnostic; in fact it doesn't specify any persistence; it could be anything: SQL database, xml file, remote service, an alien from outer space etc.
For searching capabilities, the Repository constructs an Selector which can be filtered, LIMIT-ed, sorted and counted. In the end, the selector fetches one or more Entities from the persistence.
Here is some sample code:
<?php
interface Repository
{
public function addEntity(Entity $entity);
public function updateEntity(Entity $entity);
public function removeEntity(Entity $entity);
/**
* #return Entity
*/
public function loadEntity($entityId);
public function factoryEntitySelector():Selector
}
interface Selector extends \Countable
{
public function count();
/**
* #return Entity[]
*/
public function fetchEntities();
/**
* #return Entity
*/
public function fetchEntity();
public function limit(...$limit);
public function filter(Filter $filter);
public function orderBy($column, $ascending = true);
public function removeFilter($filterName);
}
interface Filter
{
public function getFilterName();
}
Then, one implementation:
class SqlEntityRepository
{
...
public function factoryEntitySelector()
{
return new SqlSelector($this);
}
...
}
class SqlSelector implements Selector
{
...
private function adaptFilter(Filter $filter):SqlQueryFilter
{
return (new SqlSelectorFilterAdapter())->adaptFilter($filter);
}
...
}
class SqlSelectorFilterAdapter
{
public function adaptFilter(Filter $filter):SqlQueryFilter
{
$concreteClass = (new StringRebaser(
'Filter\\', 'SqlQueryFilter\\'))
->rebase(get_class($filter));
return new $concreteClass($filter);
}
}
The ideea is that the generic Selector uses Filter but the implementation SqlSelector uses SqlFilter; the SqlSelectorFilterAdapter adapts a generic Filter to a concrete SqlFilter.
The client code creates Filter objects (that are generic filters) but in the concrete implementation of the selector those filters are transformed in SQL filters.
Other selector implementations, like InMemorySelector, transform from Filter to InMemoryFilter using their specific InMemorySelectorFilterAdapter; so, every selector implementation comes with its own filter adapter.
Using this strategy my client code (in the bussines layer) doesn't care about a specific repository or selector implementation.
/** #var Repository $repository*/
$selector = $repository->factoryEntitySelector();
$selector->filter(new AttributeEquals('activated', 1))->limit(2)->orderBy('username');
$activatedUserCount = $selector->count(); // evaluates to 100, ignores the limit()
$activatedUsers = $selector->fetchEntities();
P.S. This is a simplification of my real code
I'll add a bit on this as I am currently trying to grasp all of this myself.
#1 and 2
This is a perfect place for your ORM to do the heavy lifting. If you are using a model that implements some kind of ORM, you can just use it's methods to take care of these things. Make your own orderBy functions that implement the Eloquent methods if you need to. Using Eloquent for instance:
class DbUserRepository implements UserRepositoryInterface
{
public function findAll()
{
return User::all();
}
public function get(Array $columns)
{
return User::select($columns);
}
What you seem to be looking for is an ORM. No reason your Repository can't be based around one. This would require User extend eloquent, but I personally don't see that as a problem.
If you do however want to avoid an ORM, you would then have to "roll your own" to get what you're looking for.
#3
Interfaces aren't supposed be hard and fast requirements. Something can implement an interface and add to it. What it can't do is fail to implement a required function of that interface. You can also extend interfaces like classes to keep things DRY.
That said, I'm just starting to get a grasp, but these realizations have helped me.
I can only comment on the way we (at my company) deal with this. First of all performance is not too much of an issue for us, but having clean/proper code is.
First of all we define Models such as a UserModel that uses an ORM to create UserEntity objects. When a UserEntity is loaded from a model all fields are loaded. For fields referencing foreign entities we use the appropriate foreign model to create the respective entities. For those entities the data will be loaded ondemand. Now your initial reaction might be ...???...!!! let me give you an example a bit of an example:
class UserEntity extends PersistentEntity
{
public function getOrders()
{
$this->getField('orders'); //OrderModel creates OrderEntities with only the ID's set
}
}
class UserModel {
protected $orm;
public function findUsers(IGetOptions $options = null)
{
return $orm->getAllEntities(/*...*/); // Orm creates a list of UserEntities
}
}
class OrderEntity extends PersistentEntity {} // user your imagination
class OrderModel
{
public function findOrdersById(array $ids, IGetOptions $options = null)
{
//...
}
}
In our case $db is an ORM that is able to load entities. The model instructs the ORM to load a set of entities of a specific type. The ORM contains a mapping and uses that to inject all the fields for that entity in to the entity. For foreign fields however only the id's of those objects are loaded. In this case the OrderModel creates OrderEntitys with only the id's of the referenced orders. When PersistentEntity::getField gets called by the OrderEntity the entity instructs it's model to lazy load all the fields into the OrderEntitys. All the OrderEntitys associated with one UserEntity are treated as one result-set and will be loaded at once.
The magic here is that our model and ORM inject all data into the entities and that entities merely provide wrapper functions for the generic getField method supplied by PersistentEntity. To summarize we always load all the fields, but fields referencing a foreign entity are loaded when necessary. Just loading a bunch of fields is not really a performance issue. Load all possible foreign entities however would be a HUGE performance decrease.
Now on to loading a specific set of users, based on a where clause. We provide an object oriented package of classes that allow you to specify simple expression that can be glued together. In the example code I named it GetOptions. It's a wrapper for all possible options for a select query. It contains a collection of where clauses, a group by clause and everything else. Our where clauses are quite complicated but you could obviously make a simpler version easily.
$objOptions->getConditionHolder()->addConditionBind(
new ConditionBind(
new Condition('orderProduct.product', ICondition::OPERATOR_IS, $argObjProduct)
)
);
A simplest version of this system would be to pass the WHERE part of the query as a string directly to the model.
I'm sorry for this quite complicated response. I tried to summarize our framework as quickly and clear as possible. If you have any additional questions feel free to ask them and I'll update my answer.
EDIT: Additionally if you really don't want to load some fields right away you could specify a lazy loading option in your ORM mapping. Because all fields are eventually loaded through the getField method you could load some fields last minute when that method is called. This is not a very big problem in PHP, but I would not recommend for other systems.
These are some different solutions I've seen. There are pros and cons to each of them, but it is for you to decide.
Issue #1: Too many fields
This is an important aspect especially when you take in to account Index-Only Scans. I see two solutions to dealing with this problem. You can update your functions to take in an optional array parameter that would contain a list of a columns to return. If this parameter is empty you'd return all of the columns in the query. This can be a little weird; based off the parameter you could retrieve an object or an array. You could also duplicate all of your functions so that you have two distinct functions that run the same query, but one returns an array of columns and the other returns an object.
public function findColumnsById($id, array $columns = array()){
if (empty($columns)) {
// use *
}
}
public function findById($id) {
$data = $this->findColumnsById($id);
}
Issue #2: Too many methods
I briefly worked with Propel ORM a year ago and this is based off what I can remember from that experience. Propel has the option to generate its class structure based off the existing database schema. It creates two objects for each table. The first object is a long list of access function similar to what you have currently listed; findByAttribute($attribute_value). The next object inherits from this first object. You can update this child object to build in your more complex getter functions.
Another solution would be using __call() to map non defined functions to something actionable. Your __call method would be would be able to parse the findById and findByName into different queries.
public function __call($function, $arguments) {
if (strpos($function, 'findBy') === 0) {
$parameter = substr($function, 6, strlen($function));
// SELECT * FROM $this->table_name WHERE $parameter = $arguments[0]
}
}
I hope this helps at least some what.
Issue #3: Impossible to match an interface
I see the benefit in using interfaces for repositories, so I can swap
out my implementation (for testing purposes or other). My
understanding of interfaces is that they define a contract that an
implementation must follow. This is great until you start adding
additional methods to your repositories like findAllInCountry(). Now I
need to update my interface to also have this method, otherwise, other
implementations may not have it, and that could break my application.
By this feels insane...a case of the tail wagging the dog.
My gut tells me this maybe requires an interface that implements query optimized methods alongside generic methods. Performance sensitive queries should have targeted methods, while infrequent or light-weight queries get handled by a generic handler, maybe the the expense of the controller doing a little more juggling.
The generic methods would allow any query to be implemented, and so would prevent breaking changes during a transition period. The targeted methods allow you to optimize a call when it makes sense to, and it can be applied to multiple service providers.
This approach would be akin to hardware implementations performing specific optimized tasks, while software implementations do the light work or flexible implementation.
I think graphQL is a good candidate in such a case to provide a large scale query language without increasing the complexity of data repositories.
However, there's another solution if you don't want to go for the graphQL for now. By using a DTO where an object is used for carring the data between processes, in this case between the service/controller and the repository.
An elegant answer is already provided above, however I'll try to give another example that I think it's simpler and could serve as a starting point for a new project.
As shown in the code, we would need only 4 methods for CRUD operations. the find method would be used for listing and reading by passing object argument.
Backend services could build the defined query object based on a URL query string or based on specific parameters.
The query object (SomeQueryDto) could also implement specific interface if needed. and is easy to be extended later without adding complexity.
<?php
interface SomeRepositoryInterface
{
public function create(SomeEnitityInterface $entityData): SomeEnitityInterface;
public function update(SomeEnitityInterface $entityData): SomeEnitityInterface;
public function delete(int $id): void;
public function find(SomeEnitityQueryInterface $query): array;
}
class SomeRepository implements SomeRepositoryInterface
{
public function find(SomeQueryDto $query): array
{
$qb = $this->getQueryBuilder();
foreach ($query->getSearchParameters() as $attribute) {
$qb->where($attribute['field'], $attribute['operator'], $attribute['value']);
}
return $qb->get();
}
}
/**
* Provide query data to search for tickets.
*
* #method SomeQueryDto userId(int $id, string $operator = null)
* #method SomeQueryDto categoryId(int $id, string $operator = null)
* #method SomeQueryDto completedAt(string $date, string $operator = null)
*/
class SomeQueryDto
{
/** #var array */
const QUERYABLE_FIELDS = [
'id',
'subject',
'user_id',
'category_id',
'created_at',
];
/** #var array */
const STRING_DB_OPERATORS = [
'eq' => '=', // Equal to
'gt' => '>', // Greater than
'lt' => '<', // Less than
'gte' => '>=', // Greater than or equal to
'lte' => '<=', // Less than or equal to
'ne' => '<>', // Not equal to
'like' => 'like', // Search similar text
'in' => 'in', // one of range of values
];
/**
* #var array
*/
private $searchParameters = [];
const DEFAULT_OPERATOR = 'eq';
/**
* Build this query object out of query string.
* ex: id=gt:10&id=lte:20&category_id=in:1,2,3
*/
public static function buildFromString(string $queryString): SomeQueryDto
{
$query = new self();
parse_str($queryString, $queryFields);
foreach ($queryFields as $field => $operatorAndValue) {
[$operator, $value] = explode(':', $operatorAndValue);
$query->addParameter($field, $operator, $value);
}
return $query;
}
public function addParameter(string $field, string $operator, $value): SomeQueryDto
{
if (!in_array($field, self::QUERYABLE_FIELDS)) {
throw new \Exception("$field is invalid query field.");
}
if (!array_key_exists($operator, self::STRING_DB_OPERATORS)) {
throw new \Exception("$operator is invalid query operator.");
}
if (!is_scalar($value)) {
throw new \Exception("$value is invalid query value.");
}
array_push(
$this->searchParameters,
[
'field' => $field,
'operator' => self::STRING_DB_OPERATORS[$operator],
'value' => $value
]
);
return $this;
}
public function __call($name, $arguments)
{
// camelCase to snake_case
$field = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $name));
if (in_array($field, self::QUERYABLE_FIELDS)) {
return $this->addParameter($field, $arguments[1] ?? self::DEFAULT_OPERATOR, $arguments[0]);
}
}
public function getSearchParameters()
{
return $this->searchParameters;
}
}
Example usage:
$query = new SomeEnitityQuery();
$query->userId(1)->categoryId(2, 'ne')->createdAt('2020-03-03', 'lte');
$entities = $someRepository->find($query);
// Or by passing the HTTP query string
$query = SomeEnitityQuery::buildFromString('created_at=gte:2020-01-01&category_id=in:1,2,3');
$entities = $someRepository->find($query);
I suggest https://packagist.org/packages/prettus/l5-repository as vendor to implement Repositories/Criterias etc ... in Laravel5 :D
I agree with #ryan1234 that you should pass around complete objects within the code and should use generic query methods to get those objects.
Model::where(['attr1' => 'val1'])->get();
For external/endpoint usage I really like the GraphQL method.
POST /api/graphql
{
query: {
Model(attr1: 'val1') {
attr2
attr3
}
}
}
class Criteria {}
class Select {}
class Count {}
class Delete {}
class Update {}
class FieldFilter {}
class InArrayFilter {}
// ...
$crit = new Criteria();
$filter = new FieldFilter();
$filter->set($criteria, $entity, $property, $value);
$select = new Select($criteria);
$count = new Count($criteria);
$count->getRowCount();
$select->fetchOne(); // fetchAll();
So i think
I saw this code on a different question - 2nd answer link and the first comment was that it is a static factory anti-pattern and that it violates SRP:
class User {
public static function create($userid) {
// get user from the database
// set $isPartner to true or false
// set $isClient to true or false
// set $isModerator to true or false
if ($isPartner) {
return new Partner($userid);
} elseif ($isClient) {
return new Client($userid);
} elseif ($isModerator) {
return new Moderator($userid);
} else {
return new User($userid);
}
}
}
$person = User::create($userid);
Now I can understand why it violates SRP - because it deals with connecting to the database as well as building the new class, but besides that I'm not sure if I understand why it is an anti-pattern.
I wanted to write a bit of code that seemed quite similar to this so I am now wondering whether to avoid it, this is my code (in pseudo-code):
class DatabaseClass()
{
...deals with getting a result from the database...
}
abstract class User()
{
...base class for all users...
}
class AdminUser extends User(){}
class StaffUser extends User(){}
class BasicUser extends User(){}
class UserFactory()
{
function createUser($privilege)
{
if($privilege=="high")
return new AdminUser($privilege);
else if($privilege=="med")
return new StaffUser($privilege);
else
return new BasicUser($privilege);
}
$db=new DatabaseClass($username,$password);
$result=$db->getUser();
$userfactory=new UserFactory();
$user=$userfactory->createUser($result);
Now at the moment I am not using a static method but would my oop still be considered an anti-pattern?
Especially since I don't really see any difference in then doing something like this instead and it being pretty much the same thing:
$result=DatabaseClass::getUser($username,$password);
$user=UserFactory::createUser($result);
No, it's not an anti-pattern. Personally, I take the term "anti-pattern" with a grain of salt whenever I see it. It's far too easily tossed around by people who don't like your code but can't really articulate why.
The problem with a static factory is that any class which uses the factory must explicitly depend on it. That violates the principle that we should depend on abstractions rather than concretions (the 'D' in SOLID). This makes code using the factory harder to re-use and unit test. But keep in mind that the approach has benefits too. It's easier to write and easier to understand.
Your code is equivalent to a static factory method. The problem in both cases is that the caller must know the concrete class of the factory.
Your factory is not the issue, is the conection to the db i guess.
Other than that, you're using the factory method well.
Upon seen the edit, its for the best that you separate the data access from the factory.
It's not so much an "anti-pattern" as a bad idea from the maintenance perspective.
I would still be ok (not so good but still ok) if you had your data access code inside the factory as long as that code was in a separate class (that is reused across the application for data access) and you just call it to get something you need. In that case it'd be a combination of two patterns, a factory and a facade.
What i'd really pay attenntion to is to find out if the data isn't going to change during the session, if so just go one time to the db an keep the results. If you only create the User (or derived classes) once, make damm sure you only do it once.
That i think is more important that respecting patterns blindly.
PHP has functionality of dynamical class instantiation where class name that should be instantiated could be a variable. The following code works fine:
$classname='User';
$Object=new $classname; //instantiates new User()
This code instantiates that class whose name is stored in $classname variable.
I'm not so well with the Factory Pattern, however, if you want to have some benefit, it could abstract the creation based on persistence variation, e.g. if it's adapting the database for users or not.
class DatabaseUserFactory implements UserFactory
{
private $dbClass;
function __construct(DatabaseClass $dbClass)
{
$this->dbClass = $dbClass;
}
/**
* #return user
*/
function createUser()
{
$result = $db->getUser();
return $this->createUserByPrivilege($result->getPrivilege());
}
private function createUserByPrivilege($privilege)
{
if ($privilege == "high")
return new AdminUser($privilege);
else if ($privilege == "med")
return new StaffUser($privilege);
else
return new BasicUser($privilege);
}
}
$db = new DatabaseClass($username, $password);
$userfactory = new DatabaseUserFactory($db);
// ...
$user = $userfactory->createUser();
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.