I am fairly new to using separate layers for the business logic (Domain) and database access logic, but in the course of working things out I've come across a problem to which I still feel I haven't found a great solution.
Clarification My existing solution uses Data Mappers to deal with the database interactions directly. However, as I've further investigated this issue many people have suggested that the Domain layer should not directly communicate with nor contain the Data Mappers that actually perform the database interaction. This is why I placed the Repository objects between the Domain and the necessary Data Mappers but this doesn't feel quite natural or correct. So the real question is what layer naturally exists to handle communication between the Domain and the Data Mappers? Any examples of how to structure it would be appreciated.
For example:
How do I properly handle retrieving a collection of domain objects within the context of another domain object?
How do I force the insertion of a single domain object or collection of objects based on an action performed against another object. The case I'm facing currently is that when a Person is attached to a Campaign, then I need to insert all of the Events that need to be executed for that Person for that Campaign.
There is a distinction between a domain model and the implementation of it. Just because your model shows a relationship Person ---> Campaign ---> Event does not mean that you have to implement it in this way. IOW, your model shows your analysis and design in an object-oriented way, yet you implement that model in OOP which is limited in how well it can replicate that model in code.
Consider the following.
A Person is not defined by its ownership of a Campaign, so campaign can be left out of its knowledge responsibities. On the other hand, a Campaign is defined by the Events that occur as part of its execution, so it is fair to have a collection of events within a campaign. The point that I am making is that each class should have just enough behaviour and knowledge to make it whole.
As for communication between the domain and the persistence layers, consider them as two very distinct systems that are not concerned with the other. All each of them knows is what its responsiblities are and what announcements it makes. For example, the persistence layer knows how to persist data passed to it and to announce that data have been saved. However, the persistence layer does not necessarily need to understand the domain objects. Similarly, the domain layer understands Person, Campaign, and Event but knows nothing about persistence.
The implication of the above is that the domain layer needs to be a whole by itself and should not be dependent on the persistence layer for its data. However, it still needs to be supplied with data to perform its responsibilities. That data can come from either the user interface or the database and is passed to it via a third-party that knows about both domain and persistence layers.
So, in code (pseudo-C#)...
namespace DomainLayer
{
interface IDomainListener
{
void PersonCreated(Person person);
}
class Person
{
private string name;
public Person(string name)
{
this.name = name;
}
public string Name
{
get { return name; }
}
}
class Domain
{
private IDomainListener listener;
public Domain(IDomainListener listener) {
this.listener = listener;
}
public void CreatePerson(string name) {
Person person = new Person(name);
listener.PersonCreated(person);
}
}
}
namespace PersistenceLayer
{
interface IPersistenceListener
{
void PersonDataSaved(int id, object data);
}
class Persistence
{
private IPersistenceListener listener;
public Persistence(IPersistenceListener listener)
{
this.listener = listener;
}
public void SaveData(object data)
{
int id = ...; // save data and return identifier
listener.DataSaved(id, data);
}
}
}
namespace MyApplication
{
class MyController : IDomainListener, IPersistenceListener
{
public void CreatePersonButton_Clicked()
{
Domain domain = new Domain(this);
domain.CreatePerson(NameTextbox.Text);
}
public void PersonCreated(Person person)
{
Persistence persistence = new Persistence(this);
persistence.SavePersonData(person.Name);
}
public void DataSaved(int id, object data)
{
// display data on UI
}
}
}
As you can see, the namespaces represent the different tiers. The XYZListener interfaces define the announcements that are made by the XYZ tier. Any other tiers that are interested in these announcements and will respond to them need to implement these interfaces, as does our MyApplication tier.
When the "create button" is clicked, the controller creates the Domain facade object for the domain layer and registers itself as a listener. It then calls the CreatePerson method which instantiates a Person then announces that this has been done, passing the new instance. The controller responds to this announcement in the PersonCreated implementation where it spawns a facade of the persistence layer and registers itself as the listener again. It then calls the SaveData method whichannounces DataSaved when completed. The implementation of that method then displays the data on the UI.
As you can see, the domain layer and the persistence layer are each aware of only tmemselves and are not concerned with the responsibilities of the other. It is the application logic, manifested here as the controller, that wires the two together.
Back to your specific problem, you could have a method FindPerson on the persistence, which would announce PersonFound(int id). The response by the controller would be to call the persistence layer to retrieve data about campaign and events, then call the domain layer with that data to build the Person.
Sorry for the long answer...
Gabriel, this is called the "impedance matching problem." There are many solutions around, from heavyweight ones like J2EE entity beans to Ruby ActiveRecord to simply coding a hand connection.
Update
Okay, well, its hard to see exactly how to attack this without a lot more information, but here's the basic approach.
Any of these sorts of architectural issues are driven by non-functional requirements like performance; in addition, there is a correctness issue here, in that you want to make sure updates are done in the correct order. So, you're going to need to think about the workload, which is to say the pattern of usage in real-world application. With that in mind, you basically have a couple of issues: first, the base data types in your application may not map correctly to the data base (eg, what's a VARCHAR property represented as in your code?), and second your domain model may not map cleanly to your database model.
What you would like is to have the database and the dmain model work out so that one instance of a domain object is exactly a row of a table in your database model; in large-scale applications you can rarely do this because of either performance constraints or constraints imposed by a pre-existing database model.
Now, if you completely control your database model, it simplifies things somewhat, because then you can make your database model more closely resemble the domain. This might mean the database model is somewhat denormalized, but if so, you can (depending on your database) handle that with views, or just not have a completely normalized database. Normalization is a useful theoretical construct, but that doesn't mean you can't relax it in a real system.
If you don't completely control your database model, then you need a layer of objects that make the mapping. You've got a bunch of options to choose from in implementing that: you can build views or denormalized tables in the database, you can build intermediate objects, or you can do some of both, or even have several steps of both (ie, an intermediate object that accesses a denormalizaed table.)
At that point, though, you run into issues with "don't repeat yourself" and "do the simplest thing that will possibly work." Think about what is most likely to change? Your domain model? If you've got a strong domain model, that's less likely --- the business changes relatively rarely. The exact representation of data in the database? A little more common. Or, most commonly, the exact patterns of use (like discovering a need to handle concurrent updates.) So, when you think about that, what do you need to do to make it as easy as possible to deal with the most common changes.
I realize this isn't giving you very precise instructions, but I don't think we can offer precise instructions without knowing a whole lot about your applicaiton. But then I also kind of get the impression you're wondering about what the "right" way of handling this would be, while you are already working with something that more or less does the job. So, I'd end up by asking "what are you unhappy with now?" and "How would you like to solve that?"
Many systems employ an independent data layer to handle persistence to and from a database. There are several models for the organization of such a layer. Some use a sort of factory-like implementation, others employ a one-to-one mapping with one data layer class per domain class.
The model for the data layer often depends on style and preference. What is important is separating the persistence layer from the domain layer. I believe there are tools out there that will help you generate this layer, but my PHP knowledge is thin so I can't name any specifically for PHP.
I would look at the data abstraction layers used by PHPCake and Symfony.
Related
Looking through several tutorials and books regarding data access in Zend Framework, it seems as if most people do data access within their models (Active Record Pattern) or even controllers. I strongly disagree with that. Therefore I want to have a Data Access Layer (DAL) so that my domain layer remains portable by not having any "ZF stuff" in it. I have searched around but have not really found exactly what I wanted. Heads up: I am new to ZF.
DAL structure
So, the first problem is where to place the Data Access Layer. While it could certainly be placed within the library folder and adding a namespace to the autoloader, that does not seem logical as it is specific to my application (so the applications folder is suitable). I am using a modular structure. I am thinking of using the below structure:
/application/modules/default/dal/
However, I am not sure how include this folder so that I can access the classes within the controllers (without using includes/requires). If anyone knows how to accomplish this, that would be super! Any other ideas are of course also welcome.
The idea is to have my controllers interact with the Data Access Objects (DAO). Then the DAOs use models that can then be returned to the controllers. By doing this, I can leave my models intact.
Implementation
In other languages, I have previously implemented DAOs per model, e.g. DAL_User. This resulted in an awful lot of DAO classes. Is there a smarter way to do this (using a single class does not seem easy with foreign keys)?
I would also appreciate suggestions on how to implement my DAO classes in ZF. I have not spent an awful lot of time reading about all of the components available for database interaction, so any ideas are very welcome. I suspect that there is something smarter than standard PDO available (which probably uses PDO internally, though). Name drops would be sufficient.
Sorry for the many questions. I just need a push in the right direction.
Well, the first thing you have to take into account when dealing with the Data Access Layer, is that this layer also have sub-layers, it's unusual to find folders called "dal" in modern frameworks (I'm taking as basis both Zend Framework and Symfony).
Second, about ActiveRecord, you must be aware that by default Zend Frameworks doesn't implement it. Most of the tutorials take the easiest path to teach new concepts. With simple examples, the amount of business logic is minimal, so instead of adding another layer of complexity (mapping between database and model's objects) they compose the domain layer (model) with two basic patterns: Table Data Gateway and Row Data Gateway. Which is enough information for a beginner to start.
After analyzing it, you will see some similarity between ActiveRecord
and Row Data Gateway patterns. The main difference is that
ActiveRecord objects (persistable entities) carries business logic and
Row Data Gateway only represents a row in the database. If you add
business logic on a object representing a database row, then it will
become an ActiveRecord object.
Additionally, following the Zend Framework Quick Start, on the domain model section, you will realize that there's a third component, which uses the Data Mapper Pattern.
So, if the main purpose of your DAL is to map data between business objects (model) and your storage, the responsibility of this task is delegated to the Data Mappers as follows:
class Application_Model_GuestbookMapper
{
public function save(Application_Model_Guestbook $guestbook);
public function find($id);
public function fetchAll();
}
Those methods will interact with the Database Abstraction Layer and populate the domain objects with the data. Something along this lines:
public function find($id, Application_Model_Guestbook $guestbook)
{
$result = $this->getDbTable()->find($id);
if (0 == count($result)) {
return;
}
$row = $result->current();
$guestbook->setId($row->id)
->setEmail($row->email)
->setComment($row->comment)
->setCreated($row->created);
}
As you can see, the Data Mappers interacts with a Zend_Db_Table instance, which uses the Table Data Gateway Pattern. On the other hand, the $this->getDbTable->find() returns instances of the Zend_Db_Table_Row, which implements the Row Data Gateway Pattern (it's an object representing a database row).
Tip: The domain object itself, the guestbook
entity, was not created by the find() method on the DataMapper,
instead, the idea is that object creation is a task of factories
and you must inject the dependency in order to achieve the so called
Dependency Inversion Principle (DIP) (part of the SOLID principles). But that's
another subject, out of the scope of the question. I suggest you
to access the following link http://youtu.be/RlfLCWKxHJ0
The mapping stuff begins here:
$guestbook->setId($row->id)
->setEmail($row->email)
->setComment($row->comment)
->setCreated($row->created);
So far, I think I have answered your main question, your structure will be as following:
application/models/DbTable/Guestbook.php
application/models/Guestbook.php
application/models/GuestbookMapper.php
So, as in the ZF Quick Start:
class GuestbookController extends Zend_Controller_Action
{
public function indexAction()
{
$guestbook = new Application_Model_GuestbookMapper();
$this->view->entries = $guestbook->fetchAll();
}
}
Maybe you want to have a separated folder for the data mappers. Just change:
application/models/GuestbookMapper.php
to
application/models/DataMapper/GuestbookMapper.php
The class name will be
class Application_Model_DataMapper_GuestbookMapper
I've seen that you want to separate your domain model objects into modules. It's possible too, all you need is to follow the ZF's directory and namespace guidelines for modules.
Final tip: I've spent a lot of time coding my own data mappers for
finally realize that it's nightmare to maintain the object mapping when
your application grows with a lot of correlated entities. (i.e Account
objects that contain references to users objects, users that contain
roles, and so on) It's not so easy to write the mapping stuff at this
point. So I strongly recommend you, if you really want a true
object-relational mapper, to first study how legacy frameworks perform
such tasks and perhaps use it.
So, take some spare time with Doctrine 2, which is the
one of the best so far (IMO) using the DataMapper pattern.
That's it. You still can use your /dal directory for storing the DataMappers, just register the namespace, so that the auto loader can find it.
In my opinion you should have a gateway abstraction (not just Database access) per model. A DAO is not enough. What if you need to get the data from the cloud at some point? This is quickly coming a reality. If you abstract your gateway logic into something generic and then implement it using a database you can have the best of both worlds.
The implementation of a specific gateway interface could use a generic data mapper if you so chose. I work for a small company and have always just created my implementation using PDO. This lets me be close enough to the database to deal with any interesting bits of SQL I might need but am able to support a very abstracted interface.
I have not used the Zend Framework at all. I do not know if they have data-mapper tools that could help you implement the gateway interfaces.
Background
This is a long and complicated question. I'm using php/MySQL as an example (since it's an actual example) but this could theoretically apply to other languages. Bear with me.
MVC Framework with no ORM
I'm creating an application that uses its own framework. For a variety of reasons, the following answers to this question will not be accepted:
Why don't you just use X framework?
Why don't you just use X ORM?
This application does its own queries (written by me) and that's how I'd like it to stay for now.
Business Logic?
"Business Logic" seems to be a meaningless buzzword, but I take it to essentially mean
The queries and the logic that builds the result set based on those queries
I've also read that the Model in an MVC should do all the business logic.
User.php is 884 lines
Now that I've gotten my app working fairly well, I'd like to refactor it so as not to have such abominations. User.php is essentially the model for Users (obviously). There are some responsibilities I see in it that I could easily pluck out, but a major hurdle I'm running into is:
How can I reconcile SOLID with MVC?
The reason that User.php has grown so large is because I do any query that requires a User member in that file. User is used for a ton of operations (it needs to do so much more than just CRUD), so any query that needs userid, username, etc. is run by a function in this file. Apparently the queries should be in the model (and not the controller), but I feel that this should definitely be split up somehow. I need to accomplish the following:
Create an API that covers all of these necessary queries in a compartmentalized way
Avoid giving access to the DB connection class when it's not necessary
Add User data to the view (User.php is doing that right now -- the view object is injected by a setter, which I think is also bad).
...so what I could do is create other objects like UserBranchManager, UserSiteManager, UserTagManager, etc. and each of those could have the relevant queries and the DB object injected to run those queries, but then how do they get the coveted User::$userid that they need to run these queries? Not only that, but how could I pass Branch::$branchid? Shouldn't those members be private? Adding a getter for them also makes that pointless.
I'm also not sure where to draw the line of how much an object should do. A lot of the operations similar but still different. A class for each one would be huge overkill.
Possible Answer
If I can't get any help, what I'll do (or at least try to do) is have a dependency injection container of some kind build dependencies for the objects above (e.g. UserBranchManager) and inject them into the relevant controller. These would have a DB and Query object. The Query object could be passed to low level models (like User) to bind parameters as needed, and the higher level models or whatever they are called would give the results back to the controller which would add the data to the template as needed as well. Some possible hurdles I see are creating proper contracts (e.g. the UserController should preferably depend on some abstraction of the user models) but some specifics are inevitably required, especially when it comes to the view.
Can anyone offer some wisdom in response to my rambling question?
Response to #tereško
He has provided a great answer not only here, but also at How should a model be structured in MVC?
Code
As requested, here is some extremely pared down code (basically services one request). Some important notes:
Right now, controllers are not classes, just files
The controller also handles a lot of the routing
There are no "view" objects, just the templates
This will probably look really bad
These are also things to improve, but I'm mostly worried about the model (User in particular since it's getting out of control):
#usr.php -- controller
$route = route();
$user = '';
$branch = '<TRUNK>';
if (count($route) > 0) {
if (count($route) > 1) {
list($user, $branch) = $route;
}
else {
list($user) = $route;
}
}
$dec = new Decorator('user');
$dec->css('user');
if (isset($_SESSION['user']) && $_SESSION['user']->is($user)) {
$usr = $_SESSION['user'];
}
else {
$usr = new User(new DB, $user);
}
$usr->setUpTemplate($dec, $branch);
return $dec->execute();
# User.php -- model
class User {
private $userid;
private $username;
private $db;
public function __construct(DB $db, $username = null) {
$this->username = $username;
$this->db = $DB;
}
public function is($user) {
return strtolower($this->username) === strtolower($user);
}
public function setUpTemplate(Decorator $dec, $branch) {
$dec->_title = "$this->username $branch";
// This function runs a moderately complicated query
// joining the branch name and this user id/name
$dec->branch = $this->getBranchDisplay($branch);
}
}
Questions about answers
Answer here:
You talk about leaving off caching/authentication/authorization. Are these important? Why aren't they covered? How do they relate to the model/controller/router?
The Data Mapper example has the Person class with methods like getExemption, isFlaggedForAudit .. what are those? It seems like those calculations would require DB data, so how does it get them? Person Mapper leaves off select. Isn't that important?
What is "domain logic?"
Answer 5863870 (specifically the code example):
Shouldn't these factory objects be abstractions (and not rely on creation via new) or are these special?
How would your API include the necessary definition files?
I've read a lot about how it's best for dependencies to be injected in the constructor (if they're mandatory). I assume you set the factories in this way, but why not the objects/mappers/services themselves? What about abstractions?
Are you worried about code duplication (e.g. most models requiring an _object_factory member in their class definition)? If so, how could you avoid this?
You're using protected. Why?
If you can provide any specific code examples that would be best since it's easier for me to pick stuff up that way.
I understand the theory of what your answers are saying and it's helped a lot. What I'm still interested in (and not totally sure of) is making sure that dependencies of objects in this API are handled in the best way (the worst would be new everywhere).
Dont confuse SOLID (You can get a good explanation of what it is on my blog at: http://crazycoders.net/2012/03/confoo-2012-make-your-project-solid/
SOLID is great when considering the framework that goes around the application you are trying to build. The management of the data itself is another thing. You can't really apply the Single Responsibility of the S of SOLID to a business model that RELIES on other business models such as User, Groups and Permissions.
Thus, you have to let some of the SOLID go when you build an application. What SOLID is good for, is to make sure your framework behind your application is strong.
For example, if you build your own framework and business model, you will probably have a base class MODEL another for DATABASEACCESS, just remember that your MODEL shouldn't be aware of how to get the data, just know that it must get data.
For example:
Good:
- MyApp_Models_User extends MyApp_Framework_Model
- MyApp_Models_Group extends MyApp_Framework_Model
- MyApp_Models_Permission extends MyApp_Framework_Model
- MyApp_Framework_Model
- MyApp_Framework_Provider
- MyApp_Framework_MysqliProvider extends MyApp_Framework_Provider
In this good part, you create a model like this:
$user = new MyApp_Models_User(new MyApp_Framework_MysqliProvider(...));
$user->load(1234);
This way, you will prevent a fail in the single responsibility, your provider is used to load the data from one of the many providers that exist and your model represents the data that you extracted, it doesn't know how to read or write the data, thats the providers job...
Bad way:
- MyApp_Model_User
- MyApp_Model_Group
- MyApp_Model_Permission
define('MyDB', 'localhost');
define('MyUser', 'user');
define('MyPass', 'pass');
$user = new MyApp_Models_User(1234);
Using this bad method you first of all break the single responsibility, your model represents something and also manages the input/ouput of the data. Also, you create a dependency by stating that you need to define constants for the model to connect to the database and you completly abstract the database methods, if you need to change them and have 37 models, you'll have TONS of work to do...
Now, you can, if you want work the bad way... i still do it, i'm aware of it, but sometimes, when you have crappy structure and want to refactor, you can and should work against a principle just to refactor correctly and slowly, THEN, refactor a little more and end up with something SOLID and MVC looking.
Just remember that SOLID doesn't apply to everything, it's just a guideline, but it's very very good guideline.
Well .. it depends on what is actually inside your ./user.php file. If i had to guess, you would be a situation, where your user "model" has too many responsibilities. Basically, you are violating single responsibility principle and not sure how to go about fixing that.
You did no provide any code .. so , lets continue with guessing ...
It is possible that your user "model" is implementing active record pattern. This would be the main source of SRP problems. You could watch this part of lecture slides. It will explain some of it. The point would be, instead of using active record, to go with something similar to a data mapper pattern.
Also, you might notice that some of the domain logic, which works with User class instances, seems to happen outside your "model". It might be beneficial to separate that part in a different structure. Otherwise you will be forcing the domain logic inside the controller. Maybe this comment could shine some light on the whole subject.
Another thing you might have crammed inside your user "model" could be parts of the authorization (no to confuse with authentication) mechanism. It could be pragmatic to separate this responsibility.
Update
You talk about leaving off caching/authentication/authorization. Are these important? Why aren't they covered? How do they relate to the model/controller/router?
Caching is something that you would add later in the application. The domain objects do not care where the data comes from. For that reason you can wither add the caching with in the service-level objects or inside the existing data mappers. I would advise to choose former option, because changing existing mappers might have unforeseen side effects. And because it would just over-complicate the existing mappers.
namespace Application\Service;
class Community{
public function getUserDetails( $uid )
{
$user = $this->domainObjectFactory->build('User');
$cache = $this->cacheFactory->build('User');
$user->setId( $uid );
try
{
$cache->pull( $user );
}
cache( NotFoundException $e)
{
$mapper = $this->mapperFactory->build('User');
$mapper->pull( $user );
$cache->push( $user );
}
return $user->getDetails();
}
}
This would illustrate a very simplified acquisition of user information based on user's ID. The code creates domain object and provides it with ID, then this $user ovject is used as condition to search for cached details or, if it fails, fetching that pulling that information from DB via the data mapper. Also, if that is successful, the details are pushed into the cache, for next time.
You might notice, that this example did not handle situation, when mapper cannot find such user with such ID in storage (usually - SQL database). As I said , it's a simplified example.
Also, you might notice, that this sort of caching can be easily added on case-by-case basis and would not drastically change how your logic behaves.
Authorization is another part, which should not directly influence your business logic. I already linked my preferred way for providing authentication. The idea is that, instead of checking for credentials inside controller (like here, here, here or here), the access rights are checked before you execute a method on the controller. This way you have additional options for handling the denial of access, without being stuck within a specific controller.
The Data Mapper example has the Person class with methods like getExemption(), isFlaggedForAudit() .. what are those? It seems like those calculations would require DB data, so how does it get them? Person Mapper leaves off select. Isn't that important?
The Person class is a domain object. It would contain the part of domain logic, that is associated directly with that entity.
For those methods to be executed, the mapper should at first load the data. In PHP it would look something like this:
$person = new Person;
$mapper = new PersonMapper( $databaseConnection );
$person->setId( $id );
$mapper->fetch( $person );
if ( $person->isFlaggedForAudit() )
{
return $person->getTaxableEearnings();
}
The names of methods in the PersonMapper are there as an example, so that you would understand, how the class is supposed to be used. I usually name methods fetch(), store() and remove() (or push/pull/remove ... depends on how much GIT have I been using). IMHO, there is no point to have a separate update() and insert() methods. If object's data was initially retrieved by mapper, then it is an UPDATE. If not - it is an INSERT. You can also determine it whether the value, which corresponds to PRIMARY KEY has been set or not (in some cases, at least).
What is "domain logic?"
It is part of the code which knows how to create an invoice and apply discount price for specific products. It's also the code which makes sure, that you do not submit registration form, you do not state that you have been born in 1592.
The MVC is made from two layers: presentation (can contain: views, templates, controllers) and model layer (can contain: services, domain objects, mappers). The presentation layer deals with user interaction and responses. The model layer deals with business and validation rules. You could say that domain business logic is everything in model, that does not deal with storage.
I guess there is no easy way to explain.
Shouldn't these factory objects be abstractions (and not rely on creation via new) or are these special?
Which "factory objects", where? Are you talking about this fragment ?
$serviceFactory = new ServiceFactory(
new DataMapperFactory( $dbhProvider ),
new DomainObjectFactory
);
$serviceFactory->setDefaultNamespace('Application\\Service');
That whole code fragment is supposed to be in bootstrap.php file (or you might be using index.php or init.php for that). It's the entry point for the application. It is not part of any class, therefore you cannot have *tight coupling" there. There is nothing to "couple with".
How would your API include the necessary definition files?
That fragment is not entire bootstrap.php file. Above that code are includes and initialization for autoloader. I am currently using dynamic loader, which lets classes from same namespace to be located in different folders.
Also, such autoloader is a development-stage artifact. In production code you have to use loader, which works with predefined hashtable and does not need to actually walk the tree of namespaces and locations.
I've read a lot about how it's best for dependencies to be injected in the constructor (if they're mandatory). I assume you set the factories in this way, but why not the objects/mappers/services themselves? What about abstractions?
What are you talking about ?!?
Are you worried about code duplication (e.g. most models requiring an _object_factory member in their class definition)? If so, how could you avoid this?
Did you actually LOOK at how old that code fragment in comments was ?!?!?
You're using protected. Why?
Because, if values/methods are defined with protected visibility, you can access them when you extend the original class. Also, that code example was made for old version of answer. Check the dates.
And no. I will not provide any specific code examples. Because each situation is different. If you want to do copy-paste development, then use CakePHP or CodeIgniter.
I'm in the process of designing the base architecture of a web application. The project follows the Domain-Driven Design approach because the business model and logic is very complex.
The project also aims to be a SOA project (Service Oriented Architecture). So I'm learning a lot about Services and how to construct the project around it.
Following a previous question of mine, I have a question regarding associations in model classes.
I understand that model classes shouldn't know and do anything related to persistence. However I have trouble deciding for situations with association between model classes.
For example:
class Person
class Car has one driver (for the example)
Where should the getDriver and getCars be?
in the model classes: $car->getDriver()
in the service layer with primitive types: $personService->getPerson($car->getDriverId())
in the service layer using OOP: $carService->getDriver($car)
Solution 1. seems the more natural. I'm using Doctrine 2, so the model associations are handled with DB mapping annotations. That way, the model doesn't do anything related to persistence (even though it does through Doctrine actually). It's my favorite solution, but then what's the point of the Service except load the list of "cars" to start with?
Solution 2. seems just stupid because it throws away OOP and the Model/Service user has to know about the Database model to fetch association (he has to know that this ID is a "Person" id). And he has to do the association himself.
Solution 3. is a bit better than solution 2, but still where is the OOP in that?
So, for me solution 1. is the best. But I have seen Solution 2. and Solution 3. used in real projects (sometimes mixed together), and so I have doubts.
And the question becomes more complex when there are additional parameters, for example:
$person->getCars($nameFilter, $maxNumberOfResults, $offset);
(in this case, it really looks like a SQL query/persistence query)
So, which one should be used for a Model/Service architecture on a project following the Domain-Driven Design approach? With SOA, should my model be only "dumb" data container with no logic? If so, then where is the DDD approach?
In the context of DDD, this is a problem of deciding whether a relationship between entities is expressed via direct object association vs. repository. Both approaches can be valid and depend on the nature of the relationship. For example, in your domain, a person may have a lot of cars associated with them, and it doesn't really make sense to have a direct association to the set of cars from the person entity. Remember, the job of the entity, or more specifically the aggregate root, is to protect invariants and enforce business rules. If the set of cars associated with a person isn't required for any behavior that exists on the person class, then there is no reason to put the association on the person entity. Further more, as your example shows, the query for cars may need to be filtered. To answer your question, I would place the responsibility of representing the person-to-cars relationship in a repository. SOA is orthogonal to DDD and is more focused on how business functionality is accessed and deployed. It is informative to consider the interplay between DDD and SOA from the perspective of hexagonal architecture also called onion architecture. Your domain is at the core, encapsulated by a set of application services which form an API facade around your domain. These are different from services in SOA, which are ports/adapters in the hexagonal/onion architecture and they serve ti expose these application services as SOA services.
If your project is DDD I don't get it why you want a Model/Service architecture. IMO this creates an anemic model and everything is pretty much procedural .
Now, being DDD it means you don't care for the db. You have though (at least logically) 2 models: the domain and the persistence. The Domain model deals with the associations in the most natural way, best suitable to represent the business case. 'Has one driver' or has many it's a db centric thinking that has no place in DDD. The Persistence model handles the way the Aggregate Root will be stored in the db (here's where you define the ORM entities and their relationships et all).
About your questions, first of all it matters the context and the purpose. If it's strictly for queries (to display to a user) then a simple model can be used, no need for DDD and business rules. The controller can ask directly the specialised query repository for the data, returned as a DTO.
If you want to update the Person or a car, then in the Application Layer (I'm usually using a command based approach so in my case all these happen in a command handler, but architecturally it's still part of the Application Layer) you can retreieve the AR best suited for the task from the (domain) repository. The domain repository knows that getPerson($id) should return a domain entity as opposed to the query repository which returns a simple DTO.
$person=$repo->getPerson($id);
//do stuff
$repo->save($person);
//optionally raise event (if you're using the domain events apprach)
But what's tricky is to decide what is the AR in what context. A car has one driver in what context? A driver really belongs to a car? Is there the concept of the owner? You have the Person class, but a person can be the driver or the owner (or not if it's a rental company). As you see, it pretty much depends on the domain and only after you have a clear image of the domain you can start thinking about how do you store data and what object (entity) is returned by the repository.
When thinking about what goes where, consider the purpose of both the service and the model. Services reside in the application layer while models reside in the domain layer. So, what does your application need to know about a Person? Not much, probably. The UI will likely send some ids to process with a requested action.
Here, the AR is a Driver model. Keep in mind that services may contain other services and that Doctrine entites are POPOs and do not need to be anemic. Also, try to decouple the development thought-processes away from the persistence. For example, a $driverId does not need to be an integer, it can be any unique identifier that is relevant to the domain.
// DriverService
// If more parameters are needed, consider passing in a command object
public function beginTrip($driverId, $carId, $fromLocationId, $toLocationId)
{
$driver = $this->repository->find($driverId);
$car = $this->carService->getAvailableCar($carId, $driverId);
$withItenerary = $this->locationService->buildItenerary(
[$fromLocationId, $toLocationId]
);
$driver->drive($car, $withItenerary); // actual 'driving' logic goes here
$this->eventService->publish(new BeginTripEvent($driver, $car, $withItenerary));
}
OK first I understand I mixed SOA and application services.
True. You are mixing Domain Layer (DDD) methods with Domain Objects and Service Layer (SOA) methods with Data Transfer Objects in the question.
Domain Layer objects are different objects than Service Layer objects! For example, Service Layer may have a CarDTO object instead of Car object and DriverDTO object instead of Driver object.
$car->getDriver() is a perfectly correct way to access Driver in Domain Layer, and can also be used in a Service Layer with a restriction that wherever Service consumer requests Car data, Service always returns a Car with a Driver.
$personService->getPerson($car->getDriverId()) is valid only in Service Layer and invalid in Domain Layer. The reason for this method is Driver data is too large and complex to be always returned with a Car. Thus Service provides a separate method to request Driver data.
$carService->getDriver($car) is invalid in Domain Layer and strange to see in Service Layer, because this construction means that Service consumer must send all Car data to the a CarService to get Driver data. It is better to send only CarID and maybe to a PersonService, not to a CarService (Variant 2).
A more complex example $person->getCars($nameFilter, $maxNumberOfResults, $offset); looks strange in Domain Layer, since it doesn't contain much Business Logic. But if changed into $CarService->getCars($nameFilter, $maxNumberOfResults, $offset); it becomes suitable in Service Layer for partial requests.
The last few days, I have extensively read books and web pages about OOP and MVC in PHP, so that I can become a better programmer. I've come upon a little problem in my understanding of MVC:
Where do I put a mysql_query?
Should I put it in the controller and call a method on a model that returns data based on the provided query? Or should I put it in the model itself? Are both of the options I'm providing total garbage?
Materials on the subject of MVC
You could have listed the books you were reading, because most (if not all) php books, which touch on MVC, are wrong.
If you want to become a better developer, i would recommend for you to start with article by Marting Fowler - GUI Architectures. Followed by book from same author - "Patterns of Enterprise Application Architecture". Then the next step would be for you to research SOLID principles and understand how to write code which follows Law of Demeter. This should cover the basics =]
Can I use MVC with PHP ?
Not really. At least not the classical MVC as it was defined for Smalltalk.
Instead in PHP you have 4 other patterns which aim for the same goal: MVC Model2, MVP, MVVM and HMVC. Again, I am too lazy to write about differences one more time, so I'll just link to an old comment of mine.
What is Model ?
First thing you must understand is that Model in MVC is not a class or an object. It is a layer which contains multitude of classes. Basically model layer is all of the layers combined (though, the second layer there should be called "Domain Object Layer", because it contains "Domain Model Objects"). If you care to read quick summary on what is contained in each part of Model layer, you can try reading this old comment (skip to "side note" section).
The image is taken from Service Layer article on Fowler's site.
What does the Controllers do ?
Controller has one major responsibilities in MVC (I'm gonna talk about Model2 implementation here):
Execute commands on structures from model layer (services or domain objects), which change the state of said structures.
It usually have a secondary responsibility: to bind (or otherwise pass) structures from Model layer to the View, but it becomes a questionable practice, if you follow SRP
Where do I put SQL related code ?
The storage and retrieval of information is handled at the Data Source Layer, and is usually implemented as DataMapper (do not confuse with ORMs, which abuse that name).
Here is how a simplified use of it would look like:
$mapper = $this->mapperFactory->build(Model\Mappers\User::class);
$user = $this->entityFactory->build(Model\Entities\User::class);
$user->setId(42);
$mapper->fetch($user);
if ($user->isBanned() && $user->hasBannExpired()){
$user->setStatus(Model\Mappers\User::STATUS_ACTIVE);
}
$mapper->store($user);
As you see, at no point the Domain Object is even aware, that the information from it was stored. And neither it cases about where you put the data. It could be stored in MySQL or PostgreSQL or some noSQL database. Or maybe pushed to remote REST API. Or maybe the mapper was a mock for testing. All you would need to do, to replace the mapper, is provide this method with different factory.
Also, please see these related posts:
understanding MVC Views in PHP
testable Controllers with dependencies
how should services communicate between each other?
MVC for advanced PHP developers
Model and Entity Classes represents the data and the logic of an application, what many calls business logic. Usually, it’s responsible for:
Storing, deleting, updating the application data. Generally it includes the database operations, but implementing the same operations invoking external web services or APIs is not an unusual at all.
encapsulating the application logic. This is the layer that
should implement all the logic of the application
Here is the MVC Sequence Diagram which shows the flow during a http request:
In this case Model is the best place to implement the code realted to access database.
The model contains the domain objects or data structures that represent the application's state. [wikipedia]. So the model would be the place to make the database call.
In the 'classic' (lack of a better word atm) MVC pattern the view would get the current state from the model.
Don't make the mistake by saying that the model is for accessing the database. It's more than just accessing the database.
For one, don't use mysql_query() and family; they're being deprecated, so consider also learning about PDO and/or mysqli.
The model takes care of data handling; it provides an interface to the controller by which it retrieves and/or stores information. So this would be a primary place where database actions take place.
Update
To answer a question asked by the OP in the comments: "one generic model for the whole db or a model for each table/action?"
Models are meant to abstract away individual tables (although there are models that exclusively handle a single table); for instance, instead of asking for all articles and then query the usernames for the authors you would have one function like this:
function getArticles()
{
// query article table and join with user table to get username
}
How many models you will create largely depends on how big the project is and how inter-related the data is. If you can identify independent groups of data, it's likely that you'd create a model for each group; but this is no hard & fast rule.
Data manipulation can be part of the same model, unless you want a clear separation between read-only and write-only models (I wouldn't know of a situation that warrants this, but who knows).
To go even further, your model should not contain the database access code. This belongs to another layer outside the Model/View/Controller: this is called the persistence layer, which can be implemented using an Object-Relational Mapper such as the popular Doctrine 2 for PHP.
This way, you never touch any (my)SQL code. The persistence layer takes care of this for you.
I really advise you to have a look at a Doctrine tutorial, this is a really professional way to create your applications.
Instead of working with raw data loaded from the database, you create objects that hold your data, and the behavior associated with it.
For example, you might have a User class, such as:
class User
{
protected $id;
protected $name;
protected $privileges;
public function setName($name) { ... }
public function getName() { ... }
public function addPrivilege(Privilege $privilege) { ... }
public function getPrivileges() { ... }
}
You controller will only interact with objects:
class UserController
{
public function testAction()
{
// ...
$user = $em->getRepository('User')->find(123); // load User with id 123
$user->setName('John'); // work with your objects,
echo $user->getName(); // and don't worry about the db!
$em->flush(); // persist your changes
}
}
Behind the scenes, the ORM takes care of all the low-level work of issuing a SELECT query, instantiating your object, detecting modifications to your object, and issuing the necessary UPDATE statement!
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I am just getting a grasp on the MVC framework and I often wonder how much code should go in the model. I tend to have a data access class that has methods like this:
public function CheckUsername($connection, $username)
{
try
{
$data = array();
$data['Username'] = $username;
//// SQL
$sql = "SELECT Username FROM" . $this->usersTableName . " WHERE Username = :Username";
//// Execute statement
return $this->ExecuteObject($connection, $sql, $data);
}
catch(Exception $e)
{
throw $e;
}
}
My models tend to be an entity class that is mapped to the database table.
Should the model object have all the database mapped properties as well as the code above or is it OK to separate that code out that actually does the database work?
Will I end up having four layers?
Disclaimer: the following is a description of how I understand MVC-like patterns in the context of PHP-based web applications. All the external links that are used in the content are there to explain terms and concepts, and not to imply my own credibility on the subject.
The first thing that I must clear up is: the model is a layer.
Second: there is a difference between classical MVC and what we use in web development. Here's a bit of an older answer I wrote, which briefly describes how they are different.
What a model is NOT:
The model is not a class or any single object. It is a very common mistake to make (I did too, though the original answer was written when I began to learn otherwise), because most frameworks perpetuate this misconception.
Neither is it an Object-Relational Mapping technique (ORM) nor an abstraction of database tables. Anyone who tells you otherwise is most likely trying to 'sell' another brand-new ORM or a whole framework.
What a model is:
In proper MVC adaptation, the M contains all the domain business logic and the Model Layer is mostly made from three types of structures:
Domain Objects
A domain object is a logical container of purely domain information; it usually represents a logical entity in the problem domain space. Commonly referred to as business logic.
This would be where you define how to validate data before sending an invoice, or to compute the total cost of an order. At the same time, Domain Objects are completely unaware of storage - neither from where (SQL database, REST API, text file, etc.) nor even if they get saved or retrieved.
Data Mappers
These objects are only responsible for the storage. If you store information in a database, this would be where the SQL lives. Or maybe you use an XML file to store data, and your Data Mappers are parsing from and to XML files.
Services
You can think of them as "higher level Domain Objects", but instead of business logic, Services are responsible for interaction between Domain Objects and Mappers. These structures end up creating a "public" interface for interacting with the domain business logic. You can avoid them, but at the penalty of leaking some domain logic into Controllers.
There is a related answer to this subject in the ACL implementation question - it might be useful.
The communication between the model layer and other parts of the MVC triad should happen only through Services. The clear separation has a few additional benefits:
it helps to enforce the single responsibility principle (SRP)
provides additional 'wiggle room' in case the logic changes
keeps the controller as simple as possible
gives a clear blueprint, if you ever need an external API
How to interact with a model?
Prerequisites: watch lectures "Global State and Singletons" and "Don't Look For Things!" from the Clean Code Talks.
Gaining access to service instances
For both the View and Controller instances (what you could call: "UI layer") to have access these services, there are two general approaches:
You can inject the required services in the constructors of your views and controllers directly, preferably using a DI container.
Using a factory for services as a mandatory dependency for all of your views and controllers.
As you might suspect, the DI container is a lot more elegant solution (while not being the easiest for a beginner). The two libraries, that I recommend considering for this functionality would be Syfmony's standalone DependencyInjection component or Auryn.
Both the solutions using a factory and a DI container would let you also share the instances of various servers to be shared between the selected controller and view for a given request-response cycle.
Alteration of model's state
Now that you can access to the model layer in the controllers, you need to start actually using them:
public function postLogin(Request $request)
{
$email = $request->get('email');
$identity = $this->identification->findIdentityByEmailAddress($email);
$this->identification->loginWithPassword(
$identity,
$request->get('password')
);
}
Your controllers have a very clear task: take the user input and, based on this input, change the current state of business logic. In this example the states that are changed between are "anonymous user" and "logged in user".
Controller is not responsible for validating user's input, because that is part of business rules and controller is definitely not calling SQL queries, like what you would see here or here (please don't hate on them, they are misguided, not evil).
Showing user the state-change.
Ok, user has logged in (or failed). Now what? Said user is still unaware of it. So you need to actually produce a response and that is the responsibility of a view.
public function postLogin()
{
$path = '/login';
if ($this->identification->isUserLoggedIn()) {
$path = '/dashboard';
}
return new RedirectResponse($path);
}
In this case, the view produced one of two possible responses, based on the current state of model layer. For a different use-case you would have the view picking different templates to render, based on something like "current selected of article" .
The presentation layer can actually get quite elaborate, as described here: Understanding MVC Views in PHP.
But I am just making a REST API!
Of course, there are situations, when this is a overkill.
MVC is just a concrete solution for Separation of Concerns principle. MVC separates user interface from the business logic, and it in the UI it separated handling of user input and the presentation. This is crucial. While often people describe it as a "triad", it's not actually made up from three independent parts. The structure is more like this:
It means, that, when your presentation layer's logic is close to none-existent, the pragmatic approach is to keep them as single layer. It also can substantially simplify some aspects of model layer.
Using this approach the login example (for an API) can be written as:
public function postLogin(Request $request)
{
$email = $request->get('email');
$data = [
'status' => 'ok',
];
try {
$identity = $this->identification->findIdentityByEmailAddress($email);
$token = $this->identification->loginWithPassword(
$identity,
$request->get('password')
);
} catch (FailedIdentification $exception) {
$data = [
'status' => 'error',
'message' => 'Login failed!',
]
}
return new JsonResponse($data);
}
While this is not sustainable, when you have complicate logic for rendering a response body, this simplification is very useful for more trivial scenarios. But be warned, this approach will become a nightmare, when attempting to use in large codebases with complex presentation logic.
How to build the model?
Since there is not a single "Model" class (as explained above), you really do not "build the model". Instead you start from making Services, which are able to perform certain methods. And then implement Domain Objects and Mappers.
An example of a service method:
In the both approaches above there was this login method for the identification service. What would it actually look like. I am using a slightly modified version of the same functionality from a library, that I wrote .. because I am lazy:
public function loginWithPassword(Identity $identity, string $password): string
{
if ($identity->matchPassword($password) === false) {
$this->logWrongPasswordNotice($identity, [
'email' => $identity->getEmailAddress(),
'key' => $password, // this is the wrong password
]);
throw new PasswordMismatch;
}
$identity->setPassword($password);
$this->updateIdentityOnUse($identity);
$cookie = $this->createCookieIdentity($identity);
$this->logger->info('login successful', [
'input' => [
'email' => $identity->getEmailAddress(),
],
'user' => [
'account' => $identity->getAccountId(),
'identity' => $identity->getId(),
],
]);
return $cookie->getToken();
}
As you can see, at this level of abstraction, there is no indication of where the data was fetched from. It might be a database, but it also might be just a mock object for testing purposes. Even the data mappers, that are actually used for it, are hidden away in the private methods of this service.
private function changeIdentityStatus(Entity\Identity $identity, int $status)
{
$identity->setStatus($status);
$identity->setLastUsed(time());
$mapper = $this->mapperFactory->create(Mapper\Identity::class);
$mapper->store($identity);
}
Ways of creating mappers
To implement an abstraction of persistence, on the most flexible approaches is to create custom data mappers.
From: PoEAA book
In practice they are implemented for interaction with specific classes or superclasses. Lets say you have Customer and Admin in your code (both inheriting from a User superclass). Both would probably end up having a separate matching mapper, since they contain different fields. But you will also end up with shared and commonly used operations. For example: updating the "last seen online" time. And instead of making the existing mappers more convoluted, the more pragmatic approach is to have a general "User Mapper", which only update that timestamp.
Some additional comments:
Database tables and model
While sometimes there is a direct 1:1:1 relationship between a database table, Domain Object, and Mapper, in larger projects it might be less common than you expect:
Information used by a single Domain Object might be mapped from different tables, while the object itself has no persistence in the database.
Example: if you are generating a monthly report. This would collect information from different of tables, but there is no magical MonthlyReport table in the database.
A single Mapper can affect multiple tables.
Example: when you are storing data from the User object, this Domain Object could contain collection of other domain objects - Group instances. If you alter them and store the User, the Data Mapper will have to update and/or insert entries in multiple tables.
Data from a single Domain Object is stored in more than one table.
Example: in large systems (think: a medium-sized social network), it might be pragmatic to store user authentication data and often-accessed data separately from larger chunks of content, which is rarely required. In that case you might still have a single User class, but the information it contains would depend of whether full details were fetched.
For every Domain Object there can be more than one mapper
Example: you have a news site with a shared codebased for both public-facing and the management software. But, while both interfaces use the same Article class, the management needs a lot more info populated in it. In this case you would have two separate mappers: "internal" and "external". Each performing different queries, or even use different databases (as in master or slave).
A view is not a template
View instances in MVC (if you are not using the MVP variation of the pattern) are responsible for the presentational logic. This means that each View will usually juggle at least a few templates. It acquires data from the Model Layer and then, based on the received information, chooses a template and sets values.
One of the benefits you gain from this is re-usability. If you create a ListView class, then, with well-written code, you can have the same class handing the presentation of user-list and comments below an article. Because they both have the same presentation logic. You just switch templates.
You can use either native PHP templates or use some third-party templating engine. There also might be some third-party libraries, which are able to fully replace View instances.
What about the old version of the answer?
The only major change is that, what is called Model in the old version, is actually a Service. The rest of the "library analogy" keeps up pretty well.
The only flaw that I see is that this would be a really strange library, because it would return you information from the book, but not let you touch the book itself, because otherwise the abstraction would start to "leak". I might have to think of a more fitting analogy.
What is the relationship between View and Controller instances?
The MVC structure is composed of two layers: ui and model. The main structures in the UI layer are views and controller.
When you are dealing with websites that use MVC design pattern, the best way is to have 1:1 relation between views and controllers. Each view represents a whole page in your website and it has a dedicated controller to handle all the incoming requests for that particular view.
For example, to represent an opened article, you would have \Application\Controller\Document and \Application\View\Document. This would contain all the main functionality for UI layer, when it comes to dealing with articles (of course you might have some XHR components that are not directly related to articles).
Everything that is business logic belongs in a model, whether it is a database query, calculations, a REST call, etc.
You can have the data access in the model itself, the MVC pattern doesn't restrict you from doing that. You can sugar coat it with services, mappers and what not, but the actual definition of a model is a layer that handles business logic, nothing more, nothing less. It can be a class, a function, or a complete module with a gazillion objects if that's what you want.
It's always easier to have a separate object that actually executes the database queries instead of having them being executed in the model directly: this will especially come in handy when unit testing (because of the easiness of injecting a mock database dependency in your model):
class Database {
protected $_conn;
public function __construct($connection) {
$this->_conn = $connection;
}
public function ExecuteObject($sql, $data) {
// stuff
}
}
abstract class Model {
protected $_db;
public function __construct(Database $db) {
$this->_db = $db;
}
}
class User extends Model {
public function CheckUsername($username) {
// ...
$sql = "SELECT Username FROM" . $this->usersTableName . " WHERE ...";
return $this->_db->ExecuteObject($sql, $data);
}
}
$db = new Database($conn);
$model = new User($db);
$model->CheckUsername('foo');
Also, in PHP, you rarely need to catch/rethrow exceptions because the backtrace is preserved, especially in a case like your example. Just let the exception be thrown and catch it in the controller instead.
In Web-"MVC" you can do whatever you please.
The original concept (1) described the model as the business logic. It should represent the application state and enforce some data consistency. That approach is often described as "fat model".
Most PHP frameworks follow a more shallow approach, where the model is just a database interface. But at the very least these models should still validate the incoming data and relations.
Either way, you're not very far off if you separate the SQL stuff or database calls into another layer. This way you only need to concern yourself with the real data/behaviour, not with the actual storage API. (It's however unreasonable to overdo it. You'll e.g. never be able to replace a database backend with a filestorage if that wasn't designed ahead.)
More oftenly most of the applications will have data,display and processing part and we just put all those in the letters M,V and C.
Model(M)-->Has the attributes that holds state of application and it dont know any thing about V and C.
View(V)-->Has displaying format for the application and and only knows about how-to-digest model on it and does not bother about C.
Controller(C)---->Has processing part of application and acts as wiring between M and V and it depends on both M,V unlike M and V.
Altogether there is separation of concern between each.
In future any change or enhancements can be added very easily.
In my case I have a database class that handle all the direct database interaction such as querying, fetching, and such. So if I had to change my database from MySQL to PostgreSQL there won't be any problem. So adding that extra layer can be useful.
Each table can have its own class and have its specific methods, but to actually get the data, it lets the database class handle it:
File Database.php
class Database {
private static $connection;
private static $current_query;
...
public static function query($sql) {
if (!self::$connection){
self::open_connection();
}
self::$current_query = $sql;
$result = mysql_query($sql,self::$connection);
if (!$result){
self::close_connection();
// throw custom error
// The query failed for some reason. here is query :: self::$current_query
$error = new Error(2,"There is an Error in the query.\n<b>Query:</b>\n{$sql}\n");
$error->handleError();
}
return $result;
}
....
public static function find_by_sql($sql){
if (!is_string($sql))
return false;
$result_set = self::query($sql);
$obj_arr = array();
while ($row = self::fetch_array($result_set))
{
$obj_arr[] = self::instantiate($row);
}
return $obj_arr;
}
}
Table object classL
class DomainPeer extends Database {
public static function getDomainInfoList() {
$sql = 'SELECT ';
$sql .='d.`id`,';
$sql .='d.`name`,';
$sql .='d.`shortName`,';
$sql .='d.`created_at`,';
$sql .='d.`updated_at`,';
$sql .='count(q.id) as queries ';
$sql .='FROM `domains` d ';
$sql .='LEFT JOIN queries q on q.domainId = d.id ';
$sql .='GROUP BY d.id';
return self::find_by_sql($sql);
}
....
}
I hope this example helps you create a good structure.