so I'm writing DataBase class which will be an encapsulation layer between PHP Controller and MySQL View.
interface iDataBase {
public function drug($action, $drug);
public function company($action, $company);
public function activeIngredient($action, $activeIngredient);
}
At First I thought of making all setters and getters seperate like getAllDrugs(), updateDrug(), removeDrug(), getForUpdate(), getDrug() and so one, but then I realised that I was polluting database interface with too much functions, plus this is a very small-scale version, I'm considering adding much-more classes and much more functionality. So, instead of using a lot of function I just setteled for 3. $action will determine what kind of thing does user want to do with certain class. so, for now, possible actions are these: "add", "getAll", "getForUpdate", "update", "remove"
but these functions masked by $action have different things to do, so their their return result is different and second argument can also be different.
Is my solution a good practice? I'm sure many of you had the same problem, how did you solve it? Are there any possible problems?
P.S. Drug, Company, ActiveIngredient are all classes
A function should have clearly defined, narrow responsibilities with clearly defined, minimalist return types. If you start to create "god functions" which do everything and the kitchen sink depending on what arguments you pass, you're going heavily into the territory of hard to maintain spaghetti code. You do not want a function that does A and returns B if you pass it X, but does C and returns D if you pass it Y etc...
It is a good idea to start concrete and generalize over time as you see similar patterns emerge. So, create the methods you actually need:
public function findUserById($id)
public function findUserByEmail($email)
public function updateCompanyName($id, $newName)
If you find you have shared code between these functions, unify the code behind the scenes to keep it DRY:
public function findUserById($id) {
return $this->find('SELECT * FROM user WHERE id = ?', $id);
}
public function findUserByEmail($email) {
return $this->find('SELECT * FROM user WHERE email = ?', $email);
}
protected function find($query, $arg) {
...
}
Don't start the other way around, thinking you "only need X,Y and Z" which seem similar enough to be unified into one method, then later finding out there are small differences between X, Y and Z and littering your code with special cases for each. That just leads to functions which are either ginormous or so general they basically do nothing on their own.
What you are likely looking for is called a TableDataGateway (emphasis mine):
A Table Data Gateway holds all the SQL for accessing a single table or view: selects, inserts, updates, and deletes. Other code calls its methods for all interaction with the database.
This means you will have one generic database adapter, for instance a PDO object. You inject this into your various TDG's. The TDG's then use that adapter to CRUD data from the database.
Example
class CompanyTableGateway
{
private $dbAdapter;
public function __construct(DBAdapter $dbAdapter)
{
$this->dbAdapter = $dbAdapter;
}
public function create($name, $street, $whatever)
{
$this->dbAdapter->exec( 'INSERT INTO companies …' );
}
public function findById($id)
{
return $this->dbAdapter->exec(
sprintf('SELECT * from companies where id = %d', $id)
);
}
// more methods …
}
If you have multiple of these Gateways, you can abstract the general CRUD logic into an Abstract class and then extend the concrete Gateways from it.
You will then use a TableModule or similar other object to call the methods on the individual Gateways.
The never ending discussion of Separation of Concerns vs Single Responsibility Principle.
Separation of Concerns (SoC) – is the process of breaking a computer program into distinct features that overlap in functionality as little as possible. A concern is any piece of interest or focus in a program. Typically, concerns are synonymous with features or behaviors.
http://en.wikipedia.org/wiki/Separation_of_concerns
Single Responsibility Principle (SRP) – every object should have a single responsibility, and that all its services should be narrowly aligned with that responsibility. On some level Cohesion is considered as synonym for SRP. http://en.wikipedia.org/wiki/Single_responsibility_principle
Try to get the best of both, and model your classes accordingly.
What I would recommend you doing is do a global database class, which controls the basic input / output of the database, and then extend this down to each table.
An example of this could be a Users table. What you can do to this table is
Create
Update
Delete
You will then extend the Super Database Class, with a Users Class, which will have getters and setters for each function you want to have, ie:
class Users extends DatabaseClass {
public function update ( $params )
{
// Make the code for an update, and let the SuperClass execute the code.
...
}
public function add ( $params )
{
...
}
public function delete ( $params )
{
...
}
}
This will allow you to later, easily add more functionality to the Users table, and optimize queries specifically for the table/data you're using.
Related
Over the past two years, I have become fairly familiar with PHP MVC style architecture, and have developed all my projects using MVC structures since then.
One question that has continued to bother me is how to group functions and database calls. I run into needing to perform the same actions across models. I would prefer not to duplicate these operations and sql query inside each of the models, but would rather group all user operations into a separate class.
For example, say I have a website with a forum, a blog, and a profile page, each with a separate model, view, and controller. However, say each of these pages needs to perform the same operation to the user table.
My Model class is constructed with a database object automatically. If I need to call a function from the user class, is it ok to pass the db object to that new User class? ... to do something like the following? I am not sure if passing objects like I am doing is fine, or is there a much better way of setting things up? Am I wasting resources, or is this a clumsy way of doing things?
Profile Model
class Profile_Model extends Model{
public function __construct() {
parent::__construct();
}
public function someFunction(){
$this->db->insert( "SOME SQL" );
$user = new User( $this->db ); // OK TO PASS DB OBJECT LIKE THIS?
$user->setSomething();
}
public function anotherFunction(){
//do something else that does not need a user object
}
}
User Class
class User{
public function __construct($db){
$this->db = $db; // OK TO SET DB OBJECT AS CLASS VARIABLE AGAIN?
}
public function setSomething(){
$this->db->insert( "SOME SQL" );
}
}
I'm trying to give you a really basic example of how I'd implement this architecture; Since it's really basic and I'm just a passionate developer and nothing more it could be I'm breaking some architectural rules, so please take it as a proof of concept.
LET'S START quickly with the Controller part where you get some request. Now you need someone that takes care of doing the dirty work.
As you can see here I'm trying to pass all the "dependencies" via constructor. These way you should be able to easily replace it with Mocks when testing .
Dependency injection is one of the concepts here.
AND NOW the Model (please remember Model is a layer and not a single class)
I've used "Services (or cases)" that should help you to compose a group of behaviors with all the actors (Classes) involved in this behavior.
Idendifying common behaviours that Services (or Cases) should do, is one of the concepts here.
Keep in mind that you should have a big picture in mind (or somewhere else depending on the project) before starting, in order to respect principle like KISS, SOLID, DRY, etc..
And please pay attention to method naming, often a bad or long name (like mine for example) is a sign that the class has more than a single Responsability or there's smell of bad design.
//App/Controllers/BlogController.php
namespace App\Controllers;
use App\Services\AuthServiceInterface;
use App\Services\BlogService;
use App\Http\Request;
use App\Http\Response;
class BlogController
{
protected $blogService;
public function __construct(AuthServiceInterface $authService, BlogService $blogService, Request $request)
{
$this->authService = $authService;
$this->blogService = $blogService;
$this->request = $request;
}
public function indexAction()
{
$data = array();
if ($this->authService->isAuthenticatedUser($this->request->getSomethingRelatedToTheUser())) {
$someData = $this->blogService->getSomeData();
$someOtherData = $this->request->iDontKnowWhatToDo();
$data = compact('someData', 'someOtherData');
}
return new Response($this->template, array('data' => $data), $status);
}
}
Now we need to create this Service that we've used in the controller. As you can see we're not talking directly with the "storage or data layer" but instead we're calling an abstraction layer that will handle that for us.
Using a Repository Pattern to retrieve data from a data layer, is one of the concepts here.
this way we can switch to whatever repository (inMemory, other storage, etc) to retrieve our data without changing the interface that the Controller is using, same method call but get data from another place.
Design by interfaces and not by concrete classes is one of the concepts here.
//App/Services/BlogService.php
<?php
namespace App\Services;
use App\Model\Repositories\BlogRepository;
class BlogService
{
protected $blogRepository;
public function __construct(BlogRepositoryInterface $blogRepository)
{
$this->blogRepository = $blogRepository;
}
public function getSomeData()
{
// do something complex with your data, here's just simple ex
return $this->blogRepository->findOne();
}
}
At this point we define the Repository that contains the persistance handler and knows about our Entity.
Again decoupling storage Persister and knowledge of an entity (what "can" be coupled with a mysql table for example), is one of the concepts here.
//App/Model/Repositories/BlogRepository.php
<?php
namespace App\Models\Respositories;
use App\Models\Entities\BlogEntity;
use App\Models\Persistance\DbStorageInterface;
class DbBlogRepository extends EntityRepository implements BlogRepositoryInterface
{
protected $entity;
public function __construct(DbStorageInterface $dbStorage)
{
$this->dbStorage = $dbStorage;
$this->entity = new BlogEntity;
}
public function findOne()
{
$data = $this->dbStorage->select('*')->from($this->getEntityName());
// This should be part of a mapping logic outside of here
$this->entity->setPropA($data['some']);
return $this->entity;
}
public function getEntityName()
{
return str_replace('Entity', '', get_class($this->entity));
}
}
At the end a simple entity with Setters and Getters:
//App/Model/Entities/BlogEntity.php
<?php
namespace App\Models\Entities;
class BlogEntity
{
protected $propA;
public function setPropA($dataA)
{
$this->propA = $dataA;
}
public function getPropA()
{
return $this->propA;
}
}
AND NOW? how can you inject this classes passed as dependencies? Well, this is a long answer.
Indicatively you could use Dependency Injection as we've done here have a init/boot file where you define things like:
// Laravel Style
App::bind('BlogRepositoryInterface', 'App\Model\Repositories\DbBlogRepository');
App::bind('DbStorageInterface', 'App\Model\Persistence\PDOStorage');
or some config/service.yml file like:
// Not the same but close to Symfony Style
BlogService:
class: "Namespace\\ConcreteBlogServiceClass"
Or you may feel the need of a Container Class from where you can ask the service you need to use in your controller.
function indexAction ()
{
$blogService = $this->container->getService('BlogService');
....
Dulcis in fundo here are some useful links (You can find tons of docs about this):
Services in Domain-Driven Design
Wicked Domain Model
Dependency Injection Container
Inversion of Control and Dependency Injection
Managing common Dependencies with parent Services
Whenever you need to use an object from another class there is only one safe way to do it: Dependency Injection.
Example:
Instead of having:
public function myMethod(){
$anotherObject = new Object();
}
You should inject the object with the constructor:
function __construct($dependency) {
$this->anotherObject = $dependency;
}
Once you have this structure you can use type hint and an Inversion of Control container to build thing automatically, e.g. define:
function __construct(DependencyInterface $dependency) {
$this->anotherObject = $dependency;
}
And then set your IoC container to inject the right dependency when you need to use this object
Do you use any frameworks? If not, try having a look at some popular ones, like Zend Framework or Symfony. You'll find they solve your problem and probably many more and are a great way to expand your knowledge on how to structure your project.
That aside you are close. Although adding the database directly to your User-model is probably not want you want to do. If you can get Martin Fowler's Patterns of Enterprise Application Architecture (PEAA) you will find a whole chapter outlining how to connect your models to your database. I prefer a Gateway-class (search for the Gateway-pattern or look at Zend_Db) when building something on my own, as it is relatively easy to implement and build.
Basically you have a class which performs queries and then will pass the data to your model. Just look at Data Source Architectural Patterns in Martin Fowler's pattern catalog (http://martinfowler.com/eaaCatalog/) to get a quick glance how to structure it and definitely read the book to get a real understanding when and how to use the patterns.
I hope this helps.
Part of the answer is to use dependency injection, but there is more to it than that. Cognitively speaking, grouping starts in the mind and is teased out better by brainstorming and modeling: Entity Relationship Diagrams and UML Diagrams.
Grouping of methods into classes and delegating tasks to injected objects makes sense, but there is usually room for one level of inheritance (at minimum). The use of abstract super classes and a Strategy Pattern for child classes that inherit base functionality from the abstract parent can help reduce code duplication (DRY).
All that being said, this is one reason why dependency injection containers are popular. They allow you to obtain the objects, and hence functionality, you need anywhere, without coupling object instantiation to usage.
Do a search for Pimple in Google. It may give you some ideas.
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 know there are loads of questions on this, I have done quite a bit of reading. I'd like to ask this in context of my project to see what suggestions you may have.
I have quite a large web application with many classes, e.g. users and articles (which i consider to be the main classes) and smaller classes such as images and comments. Now on a page, lets say for example an article, it could contain many instances of images and comments. Makes sense right? Now on say an articles page I call a static method which returns an array of article objects.
That's the background, so here are the questions.
Since building a large amount of the app I came to realise it would be very useful to have a core system class containing settings and shared functions. There for I extended all of my classes with a new core class. Seemed relatively simple and quick to implement. I know CodeIgniter does something similar. I feel now though my app is becoming a bit messy.
Question Is this a good idea? Creating an instance of core is exactly what I want when calling an instance of an article, but what about when i'm creating multiple instances using the static method, or calling multiple images or comments on a page. I'm calling the core class unnecessarily right? Really it only needs to be called once per page (for example the constructor defines various settings from the database, I don't want to this every time, only once per page obviously), but all instances of all classes should have access to that core class. Sounds exactly like I want the singleton approach, but I know that's a waste of time in PHP.
Here's an idea of what my code looks like at this point. I've tried to keep it as simple as I can.
class core {
public function __construct(){
...define some settings which are retrieve from the database
}
public function usefulFunction(){
}
}
class user extends core {
public function __construct(){
parent::__construct();
}
public function getUser($user_id){
$db = new database();
$user = /* Get user in assoc array from db */
$this->__setAll($user);
}
public static function getUsers(){
$db = new database();
$users = /* Get users from database in assoc array from db */
foreach($users as $user) {
$arrUsers[] = new self();
$arrUsers[]->__setAll($user);
}
return $arrUsers;
}
private function __setAll($attributes) {
foreach($attributes as $key => $value)
{
$this->__set($key, $value);
}
}
public function __set($key, $value) {
$this->$key = $value;
}
}
The other issue I'm having is efficiently using/sharing a database connection. Currently each method in a class requiring a database connection creates a new instance of the database, so on a page I might be doing this 5 or 10 times. Something like the dependency injection principle sounds much better.
Question Now if i'm passing the instance of the DB into the new user class, i know I need something like this...
class user{
protected $db;
public function __construct($db){
$this->db = $db;
}
... etc
}
$db = new database();
$user = new user($db);
... but when I want to run the static function users::getUsers() what is the best way to gain access to the database instance? Do i need to pass it as a variable in each static method? (there are many static methods in many classes). It doesn't seem like the best way of doing it but maybe there isn't another way.
Question If extending all of my classes off the core class as suggested in part 1, can I create an instance of the DB there and access that some how?
Question I also have various files containing functions (not oop) which are like helper files. What's the best way for these to access the database? Again i've been creating a new instance in each function. I don't really want to pass the db as a parameter to each one. Should I use globals, turn these helper files into classes and use dependency injection or something different all together?
I know there is lots of advice out there, but most info and tutorials on PHP are out of date and don't ever seem to cover something this complex...if you can call it complex?
Any suggestions on how to best layout my class structure. I know this seems like a lot, but surely this is something most developers face everyday. If you need any more info just let me know and thanks for reading!
You asked in a comment that I should elaborate why it is a bad idea. I'd like to highlight the following to answer that:
Ask yourself if you really need it.
Do design decisions for a need, not just because you can do it. In your case ask yourself if you need a core class. As you already have been asked this in comments you wrote that you actually do not really need it so the answer is clear: It is bad to do so because it is not needed and for not needing something it introduces a lot of side-effects.
Because of these side-effects you don't want to do that. So from zero to hero, let's do the following evolution:
You have two parts of code / functionality. The one part that does change, and the other part that is some basic functionality (framework, library) that does not change. You now need to bring them both together. Let's simplify this even and reduce the frame to a single function:
function usefulFunction($with, $four, $useful, $parameters)
{
...
}
And let's reduce the second part of your application - the part that changes - to the single User class:
class User extends DatabaseObject
{
...
}
I already introduced one small but important change here: The User class does not extend from Core any longer but from DatabaseObject because if I read your code right it's functionality is to represents a row from a database table, probably namely the user table.
I made this change already because there is a very important rule. Whenver you name something in your code, for example a class, use a speaking, a good name. A name is to name something. The name Core says absolutely nothing other that you think it's important or general or basic or deep-inside, or that it's molten iron. No clue. So even if you are naming for design, choose a good name. I thought, DatabaseObject and that was only a very quick decision not knowing your code even, so I'm pretty sure you know the real name of that class and it's also your duty do give it the real name. It deserves one, be generous.
But let's leave this detail aside, as it's only a detail and not that much connected to your general problem you'd like to solve. Let's say the bad name is a symptom and not the cause. We play Dr. House now and catalog the symptoms but just to find the cause.
Symptoms found so far:
Superfluous code (writing a class even it's not needed)
Bad naming
May we diagnose: Disorientation? :)
So to escape from that, always do what is needed and choose simple tools to write your code. For example, the easiest way to provide the common functions (your framework) is as easy as making use of the include command:
include 'my-framework.php';
usefuleFunction('this', 'time', 'really', 'useful');
This very simple tow-line script demonstrates: One part in your application takes care of providing needed functions (also called loading), and the other part(s) are using those (that is just program code as we know it from day one, right?).
How does this map/scale to some more object oriented example where maybe the User object extends? Exactly the same:
include 'my-framework.php';
$user = $services->store->findUserByID($_GET['id']);
The difference here is just that inside my-framework.php more is loaded, so that the commonly changing parts can make use of the things that don't change. Which could be for example providing a global variable that represents a Service Locator (here $services) or providing auto-loading.
The more simple you will keep this, the better you will progress and then finally you will be faced with real decisions to be made. And with those decisions you will more directly see what makes a difference.
If you want some more discussion / guidance for the "database class" please consider to take a read of the very good chapter about the different ways how to handle these in the book Patterns of Enterprise Application Architecture which somewhat is a long title, but it has a chapter that very good discusses the topic and allows you to choose a fitting pattern on how to access your database quite easily. If you keep things easy from the beginning, you not only progress faster but you are also much easier able to change them later.
However if you start with some complex system with extending from base-classes (that might even do multiple things at once), things are not that easily change-able from the beginning which will make you stick to such a decision much longer as you want to then.
You can start with an abstract class that handles all of your Database queries, and then constructs them into objects. It'll be easy to set yourself up with parameterized queries this way, and it will standardize how you interact with your database. It'll also make adding new object models a piece of cake.
http://php.net/manual/en/language.oop5.abstract.php
abstract class DB
{
abstract protected function table();
abstract protected function fields();
abstract protected function keys();
public function find()
{
//maybe write yourself a parameterized method that all objects will use...
global $db; //this would be the database connection that you set up elsewhere.
//query, and then pack up as an object
}
public function save()
{
}
public function destroy()
{
}
}
class User extends DB
{
protected function table()
{
//table name
}
protected function fields()
{
//table fields here
}
protected function keys()
{
//table key(s) here
}
//reusable pattern for parameterized queries
public static function get_user( $id )
{
$factory = new User;
$params = array( '=' => array( 'id' => $id ) );
$query = $factory->find( $params );
//return the object
}
}
You'll want to do your database connection from a common configuration file, and just leave it as a global variable for this pattern.
Obviously this is just scratching the surface, but hopefully it gives you some ideas.
Summarize all answers:
Do not use single "God" class for core.
It's better to use list of classes that make their jobs. Create as many class as you need. Each class should be responsible for single job.
Do not use singletones, it's old technique, that is not flexible, use dependecy injection container (DIC) instead.
First, the the best thing to do would be to use Singleton Pattern to get database instance.
class Db{
protected $_db;
private function __construct() {
$this->_db = new Database();
}
public static function getInstance() {
if (!isset(self::$_db)) {
self::$_db = new self();
}
return self::$_db;
}
}
Now you can use it like db::getInstance(); anywhere.
Secondly, you are trying to invent bicycle called Active Record pattern, in function __setAll($attributes).
In third, why do you wrote this thing in class that extends Core?
public function __construct(){
parent::__construct();
}
Finally, class names should be capitalized.
Or in more specific words, is it "ok" to not be relying on setters and getters?
I'm dealing with a class that checks the availability of rooms and sets public properties of which there are more than a dozen. Things such as:
unitNumber
roomTypes ( array )
codeCorporate
codeGroup
numberKids
numberAdults
numberRooms
currency
minRate
maxRate
soapServer
units ( array )
hotelId
And after an object is instantiated those properties are set with $this-> inside various methods. However the code that deals with the object often sets public properties directly instead of using getter/setter methods:
$object->something = 3;
foreach ($object->things as $thing ) { }
If I have the time to refactor this class..
Should I stick all of these properties in a data array that's a private property, and define __set and __get methods?
Should I make a single getter method for each of the properties?
In my opinion, it is rarely a good idea to have any public members. It increases coupling between classes, and makes refactoring very complicated (should you need it.)
Setters/Getters are the way to go, and the very small performance penalty that you pay for it is usually either optimized away, or trumped by elegance.
To answer your question about array vs. single-getter-per-var, it's a matter of taste. I tend to only keep variables of a similar type within an array, and separate the rest.
I personally have yet to find a truly good reason for a public property, though im open for suggestion :-)
Although i much prefer specified getters/setters for each property (whether that's a proxy to a generalized get($name) or not). I assume you have other code already that uses direct assignment so in that case i would say to proceed with using the magic __get/__set methods.
I think most people will recommend using setters & getters. Right now you're limited to simply setting & fetching the property, but what if you want to log when that property is accessed? Or perhaps you want to run the value by a validation function first (email, phonenumber, zip code, etc). Maybe you'll need to call another function, or set another property. I think you see where I'm heading with this. By using setters & getters, you add a valuable layer of encapsulation to your classes, and 99% of the time this is worth the extra typing you'll need to do ;) Imagine trying to do the examples above without setters & getters. It'd be a big headache to say the least.
Edit: I forgot to mention Doctrine. It's an object relation mapper (ORM) that can automatically setup setters & getters for you (amongst other things). You can check it out at http://www.doctrine-project.org/
I would take a step back and ask some more general questions:
Why am I having to expose this much information; what is using it and why?
Is this class really just a data structure without behavior, in which case should be a private class to some other class?
Does this class serve a single purpose, or is it on the path to becoming monolithic?
You may discover that you are able to create views of an instance of a class to export to a database, display in a form, etc. Check out the "Builder" and "Acyclic Visitor" patterns to start with.
Regarding accessors, I do not see a need to use them for what you are describing: retrieving class properties and internal state information, aka a struct. However, for attributes of a class I could see the benefit in certain cases, but more for retrieving attributes, not for mutations of your object's state.
If I may add my grain of salt several months later :
It is very un-OO to have public properties. Everything should be encapsulated, simply because (among other reasons) using direct attribute manipulation doesn't give you ways to easily refactor or perform (more) control checks when some external source modifies the field. For example, let's say you have a class with many fields that is used throughout a project several times, and that project contains several thousands of files; it's a project that has been running and expanded for a few years now. Let's say that the company is changing it's business model, or that a problem is found with some of the field's data type and now is required to have some validation; will you duplicate that validation in all those thousands of source code that is directly accessing the public member? In PHP, the solution may be simple, but not in most OO programming language (i.g. Java). The fact is that OO is based on encapsulation. In short, encapsulation doesn't only produce clean code, but also highly maintainable (not to say cost-effective and cohesive) code.
Your suggestion of having a private member (array) being manipulated by __get / __set is good. This way, if you need some extra validation along the road, simply create your setter and/or your getter and it will be the end of it. Some may argue with that being counter productive as the code completion cannot kick-in on __get / __set. IMHO, relying on code completion is simply lazy coding. But then again, having every member have it's own getter and/or setter allows you to write a more comprehensive API documentation. Personally, I usually use that technique for internal or very general purpose classes. If all your fields do not require any validation, or there are as you said several dozen of them, then using magic methods would be acceptable, in my opinion.
The bottom line is to avoid direct member access on class instances, period. How you decide to achieve this is strictly up to you. Just make sure that the API is well documented the more abstract you make it.
On a final note, in PHP, if you already have classes that are being used that are not encapsulating their fields, for example something like
class SomeObject {
public $foo;
public $bar;
public $baz;
//...
}
you can simply fix this class without having to refactor anything with something like :
class SomeObject {
private $_foo; // having underscore as prefix helps to know what's private/protected
private $_bar; // inside the code.
private $_baz;
public function __get($name) {
$methodName = 'get'.ucfirst($name);
if (method_exists($this, $methodName)) {
return $this->{$methodName}();
} else {
throw new Exception("Method '{$methodName}' does not exist");
}
}
public function __set($name, $value) {
$methodName = 'set'.ucfirst($name);
if (method_exists($this, $methodName)) {
$this->{$methodName}($value);
} else {
throw new Exception("Method '{$methodName}' does not exist");
}
}
public function getFoo() { return $this->_foo; }
public function setFoo($value) { $this->_foo = $value; }
public function getBar() { return $this->_bar; }
public function setBar($value) { $this->_bar = $value; }
public function getBaz() { return $this->_baz; }
public function setBaz($value) { $this->_baz = $value; }
}
And then
$obj = new SomeObject();
$obj->foo = 'Hello world'; // legacy code support
$obj->setFoo('Hello world'); // same thing, but preferred
And you satisfy both the OO paradigm and having direct access to attributes of an instance. You could also have __call() check for prefix 'get' or 'set' and call __get() and __set() accordingly, but I would not go that far, though this would truly enable general purpose classes to access it's private members via ->member and ->getMember()/->setMember()
I know that in OOP you want every object (from a class) to be a "thing", eg. user, validator etc.
I know the basics about MVC, how they different parts interact with each other.
However, i wonder if the models in MVC should be designed according to the traditional OOP design, that is to say, should every model be a database/table/row (solution 2)?
Or is the intention more like to collect methods that are affecting the same table or a bunch of related tables (solution 1).
example for an Address book module in CodeIgniter, where i want be able to "CRUD" a Contact and add/remove it to/from a CRUD-able Contact Group.
Models solution 1: bunching all related methods together (not real object, rather a "package")
class Contacts extends Model {
function create_contact() {)
function read_contact() {}
function update_contact() {}
function delete_contact() {}
function add_contact_to_group() {}
function delete_contact_from_group() {}
function create_group() {}
function read_group() {}
function update_group() {}
function delete_group() {}
}
Models solution 2: the OOP way (one class per file)
class Contact extends Model {
private $name = '';
private $id = '';
function create_contact() {)
function read_contact() {}
function update_contact() {}
function delete_contact() {}
}
class ContactGroup extends Model {
private $name = '';
private $id = '';
function add_contact_to_group() {}
function delete_contact_from_group() {}
function create_group() {}
function read_group() {}
function update_group() {}
function delete_group() {}
}
i dont know how to think when i want to create the models. and the above examples are my real tasks for creating an Address book. Should i just bunch all functions together in one class. then the class contains different logic (contact and group), so it can not hold properties that are specific for either one of them.
the solution 2 works according to the OOP. but i dont know why i should make such a dividing. what would the benefits be to have a Contact object for example. Its surely not a User object, so why should a Contact "live" with its own state (properties and methods). Cause i tend to think like this: If something needs a state, then i create a OOP class so that the methods could affect the state or other things based on the state.
so should models be "stateful" too? if they require no state, why should i create it according to the OOP pattern. then i could just bunch it all together like the "package" solution.
you experienced guys with OOP/MVC, please shed a light on how one should think here in this very concrete task (and in general when creating a model)
EDIT: come to think about Controllers in MVC. they are created according to the "package" solution. It makes me wonder...
should every model be a
database/table/row (solution 2)?
No. Don't tie the definition of a model to its method of persistence. Although for simple applications you might extend a model from a database row object, you should keep them at least mentally separated.
Models are simply representations of entities in your domain, so by necessity they have state. Where you talk about a Contact model, you are really talking about a mapper or gateway, i.e. the object retrieving your model from the data store. It's unfortunate that so many ad hoc implementations of Active Record have muddied the waters on this.
Mappers can be implemented as a collection of static functions or as an object - however, the collection is less flexible if you want to extend or alter the behaviour for any reason (such as mocking for unit testing).
The model itself should simply be a collection of data, either stored as public properties or preferably with appropriate setters and getters (please don't just define a get/set function pair for every variable or you might as well just leave them public), along with other methods that operate on the data. It should have no concept of, or dependency on, the data store. It is the responsibility of the mapper to instantiate and initialize the model through its interface. Doing this will afford flexibility in how you can create and save your models. You can do it from the database, an XML file, in-code, from a serialized stream sent in over the network, whatever floats your boat really, all by substituting a different mapper, and the model remains completely unaware.
I don't know if there's a best way, but i'll share the way i do it...
I have a table gateway e.g. ContactTableGateway that contains all the sql for dealing with contacts. I like all the sql being in one place.
class ContactTableGateway extends Model {
function saveContact( Contact $contact )
function getContact ( $contact_id )
function createContact ( Contact $contact )
}
Then i have a contact class that basically just has getters and setters ( or public properties ). Objects of this class are used as arguments for the table gateway to save/create
class Contact extends Model {
function getName()
function getAddress()
function getEmail()
....
}
Heres a simplified example
if ( isset( $_POST ) ) {
// some validation here
$contact = new Contact;
$contact->name = $_POST['name'];
$contact->email = $_POST['email']
$contactTableGateway = new ContactTableGateway;
if ( $contactTableGateway->createContact( $contact ) ) {
echo "YAY";
}
}
I think your Solution #2 is superior as it's more molecular/modular, therefore, more understandable, flexible and extensible (in the OOP domain). It's also more resource friendly as it could allow you to only have to load the Contact class when no contact group functionality is required and vice-versa. That is the benefit of division.
If models need no state, that does not mean OOP/MVC does not apply. Table models may have no state in one's design but that is why we have static methods/members, i.e., Contact::read($id).