0

I have a (conceptually) fairly simple application spec that I'm implementing in PHP - the large part of it consists of loading project data, displaying it, allowing a user to edit it (potentially adding sections) and then sending said data back to the database.

I'm planning on approaching it thusly:

  • Have a set of basic 'Load' objects (E.g. ProjectLoad, FormLoad) that take an ID upon creation, query the database and fill themselves with the fetched data. These objects can then be used to fill the page elements.

  • Have another set of 'Save' objects (E.g. ProjectSave, FormSave) that take arrays (returned when the page is submitted), fill themselves with that data and then perform INSERT\UPDATE operations on the database.

Is this unnecessary duplication? I'm just getting a handle on OOPHP, and all of the advice I've seen so far seems to indicate that it is best to try and keep everything (objects, methods, etc) as focussed and single-purpose as possible. This seems to meet that criteria, but is there a better way?

Damien H
  • 174
  • 11

2 Answers2

3

It looks like what you managed to brainstorm your way to two concepts:

  • the core idea behind Data Mappers

  • separation the logic for creating new entries and the logic for retrieving data (this idea is really important in CQRS, especially in context of Event Sourcing)

But I suspect that, while data mappers should be quite easy for you to grasp, the second, CQRS-related, part might be at least a year too soon for you to explore.

As for your questions..
Unless you do something stupid, there wouldn't be much duplication in your "load object" and "save objects" .. though, you probably will extract either one or two superclasses there.

The "advice you have seen" is actually called Single Responsibility Principle and is, TBH, is one is more nebulous concepts in OOP. It's like defining, what "porn" is - know it, when you see it.

And, if you want a recommendation for better approach, I would suggest to combine the read and write part in a singe data mapper. Kinda like this:

$project = new Entity\Project;
$mapper = new Mapper\Project($pdo);

$project->setId(43);
$mapper->load($project); //pulls data about project 43 from DB

if ($project->getDeadline() > time()) {
    $project->setStatus(Entity\Project::STATUS_OVERDUE);
    $mapper->save($project);  //pushes changed state to DB
}
tereško
  • 56,151
  • 24
  • 92
  • 147
  • It's always great to find out that what you've been trying to define in your head has a proper name and a deeper exploration than you had gotten down to. Data Mappers look to be just what I need - though sadly, PHP doesn't persist objects, so I can't just do a cheeky call to save($project) at the end of it all. Might put a pair of static methods in a Mapper superclass and use them to call loads by ID or from an array, much like http://stackoverflow.com/a/1701337/3171734. – Damien H Mar 22 '16 at 05:25
  • And yes, I'll bookmark the CQRS link for future reference. This site should be able to get by with CRUD, but the pointer is much appreciated. – Damien H Mar 22 '16 at 05:28
  • I would strongly recommend against use of static methods and variables. The former act as global namespaced function and latter are glorified globals. If you go the datamapper-route, then you should have one mapper per entity - separate mapper for project, separate one for user, etc. – tereško Mar 22 '16 at 05:35
  • As for automatizing saving of entities, there is actually a concept called [Unite of Work](http://www.martinfowler.com/eaaCatalog/unitOfWork.html). It's advanced structure, that is being built on top of repository and one-or-more mappers. – tereško Mar 22 '16 at 05:37
  • Yes, a mapper for each Object type is what I'm planning - I'm talking about where you have $mapper->load($project);. I'm not always going to be passing in a Project object, since after I hit submit the elements on the page will be passed to another as an array. Thus, I'll need to take that array and turn it into an object before I can use the Mapper to save it to the Database ($mapper->save($project)). – Damien H Mar 22 '16 at 05:46
  • No, you would actually need for $project to implement `getAllParameters()` method .. or something like that. – tereško Mar 22 '16 at 05:53
  • Ah, so I'd pass the Project object the array, and let it set them itself? That makes more sense, since the Mapper is meant to deal with mapping between the Object and a database - not random arrays. Sorry, it's nearly the end of the work day down here, brain's a little slow. – Damien H Mar 22 '16 at 05:57
  • The main point actually is for the domain entity (like "project" in your example) to be completely unaware, that database even exists. The mapper becomes responsible for persisting the data, that it collects from the entity and for populating the entity with data from DB. – tereško Mar 22 '16 at 06:01
  • Yes, I think I get that now. I was conflating two separate actions under one responsibility. Here's hoping all your morning's problems are as easily solved as this one. :) – Damien H Mar 22 '16 at 06:05
  • @tereško is good practise to return objects as arguments? e.x this `$mapper->load($project)` wouldnt be better practice to implement it like this: `$project = $mapper->loadAndPrepareProject($project)` ; – dios231 Mar 27 '16 at 21:09
0

I can't tell if that is really duplication since it is still a concept and I think duplication really occurs during the implementation. As a concept you have done a good job to separate the concerns among your classes.

If you really are into OOPHP you might as well check topics about Design Patterns :

According to Wikipedia:

In software engineering, a software design pattern is a general reusable solution to a commonly occurring problem ... Design patterns are formalized best practices that the programmer can use to solve common problems when designing an application or system.

javiniar.leonard
  • 528
  • 8
  • 22
  • I actually disagree with that definition. It gives an impression, that design patterns are pre-made recipes or canned solutions. IMHO, design patters are actually *shorthands*, which are used by developers **to describe already existing code**. One does not "apply patterns". Instead the "patterns emerge" from the code that you write. – tereško Mar 22 '16 at 04:18