How should a model be structured in MVC? [closed]
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?
php oop model-view-controller architecture model
closed as primarily opinion-based by TylerH, Gert Arnold, sideshowbarker, Machavity, eyllanesc Nov 22 '18 at 0:32
Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.
|
show 4 more comments
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?
php oop model-view-controller architecture model
closed as primarily opinion-based by TylerH, Gert Arnold, sideshowbarker, Machavity, eyllanesc Nov 22 '18 at 0:32
Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.
127
Why are you catching exceptions just to throw them again?
– Bailey Parker
Jun 8 '12 at 5:46
9
@Elias Van Ootegem: you missed the point. it's pointless to catch them in this case.
– Karoly Horvath
Jul 18 '12 at 20:46
4
@Elias Van Ootegem: huh? if it works with rethrow, it means that an upper layer catches the exception. But if there is one, then it would have catched it without that pointless rethrow... (if you still don't get it, please mock up a small test code)
– Karoly Horvath
Jul 18 '12 at 21:11
3
@Elias Van Ootegem: I have no idea what you're talking about, not handling an exception on a specific layer doesn't mean it will halt the app. please construct (or more precisely: fail to construct) a code example where that rethrow is necessary. let's stop this offtopic conversation, please
– Karoly Horvath
Jul 18 '12 at 22:01
6
@drrcknlsn: that's a valid argument, but in that case at least catch the exception you expect to be thrown, the genericException
doesn't have much documentation value. Personally if I went down on that road I would choose PHPDoc's@exception
, or some similar mechanism, so it shows up in the generated documentation.
– Karoly Horvath
Aug 2 '12 at 22:27
|
show 4 more comments
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?
php oop model-view-controller architecture model
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?
php oop model-view-controller architecture model
php oop model-view-controller architecture model
edited Aug 23 '14 at 18:08
i alarmed alien
8,14532035
8,14532035
asked May 3 '11 at 0:28
DietpixelDietpixel
4,72592331
4,72592331
closed as primarily opinion-based by TylerH, Gert Arnold, sideshowbarker, Machavity, eyllanesc Nov 22 '18 at 0:32
Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.
closed as primarily opinion-based by TylerH, Gert Arnold, sideshowbarker, Machavity, eyllanesc Nov 22 '18 at 0:32
Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.
127
Why are you catching exceptions just to throw them again?
– Bailey Parker
Jun 8 '12 at 5:46
9
@Elias Van Ootegem: you missed the point. it's pointless to catch them in this case.
– Karoly Horvath
Jul 18 '12 at 20:46
4
@Elias Van Ootegem: huh? if it works with rethrow, it means that an upper layer catches the exception. But if there is one, then it would have catched it without that pointless rethrow... (if you still don't get it, please mock up a small test code)
– Karoly Horvath
Jul 18 '12 at 21:11
3
@Elias Van Ootegem: I have no idea what you're talking about, not handling an exception on a specific layer doesn't mean it will halt the app. please construct (or more precisely: fail to construct) a code example where that rethrow is necessary. let's stop this offtopic conversation, please
– Karoly Horvath
Jul 18 '12 at 22:01
6
@drrcknlsn: that's a valid argument, but in that case at least catch the exception you expect to be thrown, the genericException
doesn't have much documentation value. Personally if I went down on that road I would choose PHPDoc's@exception
, or some similar mechanism, so it shows up in the generated documentation.
– Karoly Horvath
Aug 2 '12 at 22:27
|
show 4 more comments
127
Why are you catching exceptions just to throw them again?
– Bailey Parker
Jun 8 '12 at 5:46
9
@Elias Van Ootegem: you missed the point. it's pointless to catch them in this case.
– Karoly Horvath
Jul 18 '12 at 20:46
4
@Elias Van Ootegem: huh? if it works with rethrow, it means that an upper layer catches the exception. But if there is one, then it would have catched it without that pointless rethrow... (if you still don't get it, please mock up a small test code)
– Karoly Horvath
Jul 18 '12 at 21:11
3
@Elias Van Ootegem: I have no idea what you're talking about, not handling an exception on a specific layer doesn't mean it will halt the app. please construct (or more precisely: fail to construct) a code example where that rethrow is necessary. let's stop this offtopic conversation, please
– Karoly Horvath
Jul 18 '12 at 22:01
6
@drrcknlsn: that's a valid argument, but in that case at least catch the exception you expect to be thrown, the genericException
doesn't have much documentation value. Personally if I went down on that road I would choose PHPDoc's@exception
, or some similar mechanism, so it shows up in the generated documentation.
– Karoly Horvath
Aug 2 '12 at 22:27
127
127
Why are you catching exceptions just to throw them again?
– Bailey Parker
Jun 8 '12 at 5:46
Why are you catching exceptions just to throw them again?
– Bailey Parker
Jun 8 '12 at 5:46
9
9
@Elias Van Ootegem: you missed the point. it's pointless to catch them in this case.
– Karoly Horvath
Jul 18 '12 at 20:46
@Elias Van Ootegem: you missed the point. it's pointless to catch them in this case.
– Karoly Horvath
Jul 18 '12 at 20:46
4
4
@Elias Van Ootegem: huh? if it works with rethrow, it means that an upper layer catches the exception. But if there is one, then it would have catched it without that pointless rethrow... (if you still don't get it, please mock up a small test code)
– Karoly Horvath
Jul 18 '12 at 21:11
@Elias Van Ootegem: huh? if it works with rethrow, it means that an upper layer catches the exception. But if there is one, then it would have catched it without that pointless rethrow... (if you still don't get it, please mock up a small test code)
– Karoly Horvath
Jul 18 '12 at 21:11
3
3
@Elias Van Ootegem: I have no idea what you're talking about, not handling an exception on a specific layer doesn't mean it will halt the app. please construct (or more precisely: fail to construct) a code example where that rethrow is necessary. let's stop this offtopic conversation, please
– Karoly Horvath
Jul 18 '12 at 22:01
@Elias Van Ootegem: I have no idea what you're talking about, not handling an exception on a specific layer doesn't mean it will halt the app. please construct (or more precisely: fail to construct) a code example where that rethrow is necessary. let's stop this offtopic conversation, please
– Karoly Horvath
Jul 18 '12 at 22:01
6
6
@drrcknlsn: that's a valid argument, but in that case at least catch the exception you expect to be thrown, the generic
Exception
doesn't have much documentation value. Personally if I went down on that road I would choose PHPDoc's @exception
, or some similar mechanism, so it shows up in the generated documentation.– Karoly Horvath
Aug 2 '12 at 22:27
@drrcknlsn: that's a valid argument, but in that case at least catch the exception you expect to be thrown, the generic
Exception
doesn't have much documentation value. Personally if I went down on that road I would choose PHPDoc's @exception
, or some similar mechanism, so it shows up in the generated documentation.– Karoly Horvath
Aug 2 '12 at 22:27
|
show 4 more comments
5 Answers
5
active
oldest
votes
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(EntityIdentity $identity, int $status)
{
$identity->setStatus($status);
$identity->setLastUsed(time());
$mapper = $this->mapperFactory->create(MapperIdentity::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 theUser
, 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
ApplicationControllerDocument
andApplicationViewDocument
. 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).
4
@Rinzler , you will notice, that nowhere in that link, anything said about Model (except in one comment). It's only "an object-oriented interface to database tables". If you try to mold this in a Model-like thing, you end up violating SRP and LSP.
– tereško
Jun 12 '12 at 10:11
7
@hafichuk only situations, when it is reasonable to employ ActiveRecord pattern is for prototyping. When you start to write the code that is mean for production, it becomes an anti-pattern, because it mixes storage and business logic. And since Model Layer is completely unaware of the other MVC parts. This does not change depending on variation on original pattern. Even when using MVVM. There are no "multiple models" and they are not mapped to anything. Model is a layer.
– tereško
Jun 15 '12 at 14:47
3
Short Version - Models are Data Structures.
– Eddie B
Nov 24 '12 at 10:21
9
Well seeing that he invented MVC the article may have some merit.
– Eddie B
Nov 24 '12 at 10:39
3
... or even just a set of functions. MVC does not require to be implemented in an OOP style, although it is mostly implemented that way. The most important thing is to separate layers and establishing the right data and control flow
– hek2mgl
Aug 4 '13 at 11:36
|
show 63 more comments
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.
My structure is very similar, I think I just separate it out a bit more. The reason why I was passing around the connection was because I needed to have chunks run in transactions. I wanted to add a user and then add the user to a role, but role back if one failed. The only way I could sort that out was to pass the connection.
– Dietpixel
May 3 '11 at 0:50
8
-1: it also happens to be completely wrong. Model is not an abstraction for a table.
– tereško
Nov 16 '14 at 22:38
1
TheUser
class basically extends the model, but itsn't an object. User should be an object and has properties like: id, name ... You're deployingUser
class is a helper.
– TomSawyer
Dec 2 '16 at 17:43
1
I think you understand MVC but don't understand what's OOP. In this scenario, like i said,User
stands for an object, and it should have properties of an User, not methods likeCheckUsername
, what should you do if you want to create newUser
object?new User($db)
– TomSawyer
Dec 2 '16 at 18:10
@TomSawyer OOP doesn't mean objects are required to have properties. What you're describing is a design pattern, one which is irrelevant to the question or an answer to that question. OOP is a language model, not a design pattern.
– netcoder
Dec 2 '16 at 18:17
add a comment |
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.)
8
link is invalid (404)
– Kyslik
Aug 6 '13 at 9:54
1
This works from WebArchive: web.archive.org/web/20101229204648/https://stackoverflow.com/…
– Tudor
Sep 19 '18 at 13:24
add a comment |
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.
add a comment |
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.
12
"So if I had to change my database from MySQL to PostgreSQL there won't be any problem." Uhhhmmm with above code you would have a huge problem changing anything imo.
– PeeHaa
Oct 4 '12 at 19:54
I see my answer makes less and less sense after edit, and as time goes by. But it should stay here
– Ibu
Dec 19 '12 at 17:48
2
Database
in the example is not a class. It is just a wrapper for functions. Also, how can you have "table object class" without an object?
– tereško
Dec 19 '12 at 19:03
1
@tereško I have read many of your posts and they're great. But, I cannot find any complete framework anywhere to study. Do you know of one that "does it right"? Or at least one that does it like you and some others here on SO say to do? Thanks.
– johnny
Sep 1 '14 at 20:12
I may be way late, but i'd like to point out that PDO almost solves the issue of having to create a DB 'layer' in order to facilitate future changes.
– Matthew Goulart
Mar 3 '16 at 15:44
add a comment |
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
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(EntityIdentity $identity, int $status)
{
$identity->setStatus($status);
$identity->setLastUsed(time());
$mapper = $this->mapperFactory->create(MapperIdentity::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 theUser
, 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
ApplicationControllerDocument
andApplicationViewDocument
. 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).
4
@Rinzler , you will notice, that nowhere in that link, anything said about Model (except in one comment). It's only "an object-oriented interface to database tables". If you try to mold this in a Model-like thing, you end up violating SRP and LSP.
– tereško
Jun 12 '12 at 10:11
7
@hafichuk only situations, when it is reasonable to employ ActiveRecord pattern is for prototyping. When you start to write the code that is mean for production, it becomes an anti-pattern, because it mixes storage and business logic. And since Model Layer is completely unaware of the other MVC parts. This does not change depending on variation on original pattern. Even when using MVVM. There are no "multiple models" and they are not mapped to anything. Model is a layer.
– tereško
Jun 15 '12 at 14:47
3
Short Version - Models are Data Structures.
– Eddie B
Nov 24 '12 at 10:21
9
Well seeing that he invented MVC the article may have some merit.
– Eddie B
Nov 24 '12 at 10:39
3
... or even just a set of functions. MVC does not require to be implemented in an OOP style, although it is mostly implemented that way. The most important thing is to separate layers and establishing the right data and control flow
– hek2mgl
Aug 4 '13 at 11:36
|
show 63 more comments
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(EntityIdentity $identity, int $status)
{
$identity->setStatus($status);
$identity->setLastUsed(time());
$mapper = $this->mapperFactory->create(MapperIdentity::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 theUser
, 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
ApplicationControllerDocument
andApplicationViewDocument
. 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).
4
@Rinzler , you will notice, that nowhere in that link, anything said about Model (except in one comment). It's only "an object-oriented interface to database tables". If you try to mold this in a Model-like thing, you end up violating SRP and LSP.
– tereško
Jun 12 '12 at 10:11
7
@hafichuk only situations, when it is reasonable to employ ActiveRecord pattern is for prototyping. When you start to write the code that is mean for production, it becomes an anti-pattern, because it mixes storage and business logic. And since Model Layer is completely unaware of the other MVC parts. This does not change depending on variation on original pattern. Even when using MVVM. There are no "multiple models" and they are not mapped to anything. Model is a layer.
– tereško
Jun 15 '12 at 14:47
3
Short Version - Models are Data Structures.
– Eddie B
Nov 24 '12 at 10:21
9
Well seeing that he invented MVC the article may have some merit.
– Eddie B
Nov 24 '12 at 10:39
3
... or even just a set of functions. MVC does not require to be implemented in an OOP style, although it is mostly implemented that way. The most important thing is to separate layers and establishing the right data and control flow
– hek2mgl
Aug 4 '13 at 11:36
|
show 63 more comments
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(EntityIdentity $identity, int $status)
{
$identity->setStatus($status);
$identity->setLastUsed(time());
$mapper = $this->mapperFactory->create(MapperIdentity::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 theUser
, 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
ApplicationControllerDocument
andApplicationViewDocument
. 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).
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(EntityIdentity $identity, int $status)
{
$identity->setStatus($status);
$identity->setLastUsed(time());
$mapper = $this->mapperFactory->create(MapperIdentity::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 theUser
, 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
ApplicationControllerDocument
andApplicationViewDocument
. 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).
edited Oct 18 '17 at 7:54
answered May 3 '11 at 0:56
tereškotereško
52.6k2078136
52.6k2078136
4
@Rinzler , you will notice, that nowhere in that link, anything said about Model (except in one comment). It's only "an object-oriented interface to database tables". If you try to mold this in a Model-like thing, you end up violating SRP and LSP.
– tereško
Jun 12 '12 at 10:11
7
@hafichuk only situations, when it is reasonable to employ ActiveRecord pattern is for prototyping. When you start to write the code that is mean for production, it becomes an anti-pattern, because it mixes storage and business logic. And since Model Layer is completely unaware of the other MVC parts. This does not change depending on variation on original pattern. Even when using MVVM. There are no "multiple models" and they are not mapped to anything. Model is a layer.
– tereško
Jun 15 '12 at 14:47
3
Short Version - Models are Data Structures.
– Eddie B
Nov 24 '12 at 10:21
9
Well seeing that he invented MVC the article may have some merit.
– Eddie B
Nov 24 '12 at 10:39
3
... or even just a set of functions. MVC does not require to be implemented in an OOP style, although it is mostly implemented that way. The most important thing is to separate layers and establishing the right data and control flow
– hek2mgl
Aug 4 '13 at 11:36
|
show 63 more comments
4
@Rinzler , you will notice, that nowhere in that link, anything said about Model (except in one comment). It's only "an object-oriented interface to database tables". If you try to mold this in a Model-like thing, you end up violating SRP and LSP.
– tereško
Jun 12 '12 at 10:11
7
@hafichuk only situations, when it is reasonable to employ ActiveRecord pattern is for prototyping. When you start to write the code that is mean for production, it becomes an anti-pattern, because it mixes storage and business logic. And since Model Layer is completely unaware of the other MVC parts. This does not change depending on variation on original pattern. Even when using MVVM. There are no "multiple models" and they are not mapped to anything. Model is a layer.
– tereško
Jun 15 '12 at 14:47
3
Short Version - Models are Data Structures.
– Eddie B
Nov 24 '12 at 10:21
9
Well seeing that he invented MVC the article may have some merit.
– Eddie B
Nov 24 '12 at 10:39
3
... or even just a set of functions. MVC does not require to be implemented in an OOP style, although it is mostly implemented that way. The most important thing is to separate layers and establishing the right data and control flow
– hek2mgl
Aug 4 '13 at 11:36
4
4
@Rinzler , you will notice, that nowhere in that link, anything said about Model (except in one comment). It's only "an object-oriented interface to database tables". If you try to mold this in a Model-like thing, you end up violating SRP and LSP.
– tereško
Jun 12 '12 at 10:11
@Rinzler , you will notice, that nowhere in that link, anything said about Model (except in one comment). It's only "an object-oriented interface to database tables". If you try to mold this in a Model-like thing, you end up violating SRP and LSP.
– tereško
Jun 12 '12 at 10:11
7
7
@hafichuk only situations, when it is reasonable to employ ActiveRecord pattern is for prototyping. When you start to write the code that is mean for production, it becomes an anti-pattern, because it mixes storage and business logic. And since Model Layer is completely unaware of the other MVC parts. This does not change depending on variation on original pattern. Even when using MVVM. There are no "multiple models" and they are not mapped to anything. Model is a layer.
– tereško
Jun 15 '12 at 14:47
@hafichuk only situations, when it is reasonable to employ ActiveRecord pattern is for prototyping. When you start to write the code that is mean for production, it becomes an anti-pattern, because it mixes storage and business logic. And since Model Layer is completely unaware of the other MVC parts. This does not change depending on variation on original pattern. Even when using MVVM. There are no "multiple models" and they are not mapped to anything. Model is a layer.
– tereško
Jun 15 '12 at 14:47
3
3
Short Version - Models are Data Structures.
– Eddie B
Nov 24 '12 at 10:21
Short Version - Models are Data Structures.
– Eddie B
Nov 24 '12 at 10:21
9
9
Well seeing that he invented MVC the article may have some merit.
– Eddie B
Nov 24 '12 at 10:39
Well seeing that he invented MVC the article may have some merit.
– Eddie B
Nov 24 '12 at 10:39
3
3
... or even just a set of functions. MVC does not require to be implemented in an OOP style, although it is mostly implemented that way. The most important thing is to separate layers and establishing the right data and control flow
– hek2mgl
Aug 4 '13 at 11:36
... or even just a set of functions. MVC does not require to be implemented in an OOP style, although it is mostly implemented that way. The most important thing is to separate layers and establishing the right data and control flow
– hek2mgl
Aug 4 '13 at 11:36
|
show 63 more comments
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.
My structure is very similar, I think I just separate it out a bit more. The reason why I was passing around the connection was because I needed to have chunks run in transactions. I wanted to add a user and then add the user to a role, but role back if one failed. The only way I could sort that out was to pass the connection.
– Dietpixel
May 3 '11 at 0:50
8
-1: it also happens to be completely wrong. Model is not an abstraction for a table.
– tereško
Nov 16 '14 at 22:38
1
TheUser
class basically extends the model, but itsn't an object. User should be an object and has properties like: id, name ... You're deployingUser
class is a helper.
– TomSawyer
Dec 2 '16 at 17:43
1
I think you understand MVC but don't understand what's OOP. In this scenario, like i said,User
stands for an object, and it should have properties of an User, not methods likeCheckUsername
, what should you do if you want to create newUser
object?new User($db)
– TomSawyer
Dec 2 '16 at 18:10
@TomSawyer OOP doesn't mean objects are required to have properties. What you're describing is a design pattern, one which is irrelevant to the question or an answer to that question. OOP is a language model, not a design pattern.
– netcoder
Dec 2 '16 at 18:17
add a comment |
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.
My structure is very similar, I think I just separate it out a bit more. The reason why I was passing around the connection was because I needed to have chunks run in transactions. I wanted to add a user and then add the user to a role, but role back if one failed. The only way I could sort that out was to pass the connection.
– Dietpixel
May 3 '11 at 0:50
8
-1: it also happens to be completely wrong. Model is not an abstraction for a table.
– tereško
Nov 16 '14 at 22:38
1
TheUser
class basically extends the model, but itsn't an object. User should be an object and has properties like: id, name ... You're deployingUser
class is a helper.
– TomSawyer
Dec 2 '16 at 17:43
1
I think you understand MVC but don't understand what's OOP. In this scenario, like i said,User
stands for an object, and it should have properties of an User, not methods likeCheckUsername
, what should you do if you want to create newUser
object?new User($db)
– TomSawyer
Dec 2 '16 at 18:10
@TomSawyer OOP doesn't mean objects are required to have properties. What you're describing is a design pattern, one which is irrelevant to the question or an answer to that question. OOP is a language model, not a design pattern.
– netcoder
Dec 2 '16 at 18:17
add a comment |
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.
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.
edited Jun 27 '15 at 19:38
answered May 3 '11 at 0:42
netcodernetcoder
53.9k14104132
53.9k14104132
My structure is very similar, I think I just separate it out a bit more. The reason why I was passing around the connection was because I needed to have chunks run in transactions. I wanted to add a user and then add the user to a role, but role back if one failed. The only way I could sort that out was to pass the connection.
– Dietpixel
May 3 '11 at 0:50
8
-1: it also happens to be completely wrong. Model is not an abstraction for a table.
– tereško
Nov 16 '14 at 22:38
1
TheUser
class basically extends the model, but itsn't an object. User should be an object and has properties like: id, name ... You're deployingUser
class is a helper.
– TomSawyer
Dec 2 '16 at 17:43
1
I think you understand MVC but don't understand what's OOP. In this scenario, like i said,User
stands for an object, and it should have properties of an User, not methods likeCheckUsername
, what should you do if you want to create newUser
object?new User($db)
– TomSawyer
Dec 2 '16 at 18:10
@TomSawyer OOP doesn't mean objects are required to have properties. What you're describing is a design pattern, one which is irrelevant to the question or an answer to that question. OOP is a language model, not a design pattern.
– netcoder
Dec 2 '16 at 18:17
add a comment |
My structure is very similar, I think I just separate it out a bit more. The reason why I was passing around the connection was because I needed to have chunks run in transactions. I wanted to add a user and then add the user to a role, but role back if one failed. The only way I could sort that out was to pass the connection.
– Dietpixel
May 3 '11 at 0:50
8
-1: it also happens to be completely wrong. Model is not an abstraction for a table.
– tereško
Nov 16 '14 at 22:38
1
TheUser
class basically extends the model, but itsn't an object. User should be an object and has properties like: id, name ... You're deployingUser
class is a helper.
– TomSawyer
Dec 2 '16 at 17:43
1
I think you understand MVC but don't understand what's OOP. In this scenario, like i said,User
stands for an object, and it should have properties of an User, not methods likeCheckUsername
, what should you do if you want to create newUser
object?new User($db)
– TomSawyer
Dec 2 '16 at 18:10
@TomSawyer OOP doesn't mean objects are required to have properties. What you're describing is a design pattern, one which is irrelevant to the question or an answer to that question. OOP is a language model, not a design pattern.
– netcoder
Dec 2 '16 at 18:17
My structure is very similar, I think I just separate it out a bit more. The reason why I was passing around the connection was because I needed to have chunks run in transactions. I wanted to add a user and then add the user to a role, but role back if one failed. The only way I could sort that out was to pass the connection.
– Dietpixel
May 3 '11 at 0:50
My structure is very similar, I think I just separate it out a bit more. The reason why I was passing around the connection was because I needed to have chunks run in transactions. I wanted to add a user and then add the user to a role, but role back if one failed. The only way I could sort that out was to pass the connection.
– Dietpixel
May 3 '11 at 0:50
8
8
-1: it also happens to be completely wrong. Model is not an abstraction for a table.
– tereško
Nov 16 '14 at 22:38
-1: it also happens to be completely wrong. Model is not an abstraction for a table.
– tereško
Nov 16 '14 at 22:38
1
1
The
User
class basically extends the model, but itsn't an object. User should be an object and has properties like: id, name ... You're deploying User
class is a helper.– TomSawyer
Dec 2 '16 at 17:43
The
User
class basically extends the model, but itsn't an object. User should be an object and has properties like: id, name ... You're deploying User
class is a helper.– TomSawyer
Dec 2 '16 at 17:43
1
1
I think you understand MVC but don't understand what's OOP. In this scenario, like i said,
User
stands for an object, and it should have properties of an User, not methods like CheckUsername
, what should you do if you want to create new User
object? new User($db)
– TomSawyer
Dec 2 '16 at 18:10
I think you understand MVC but don't understand what's OOP. In this scenario, like i said,
User
stands for an object, and it should have properties of an User, not methods like CheckUsername
, what should you do if you want to create new User
object? new User($db)
– TomSawyer
Dec 2 '16 at 18:10
@TomSawyer OOP doesn't mean objects are required to have properties. What you're describing is a design pattern, one which is irrelevant to the question or an answer to that question. OOP is a language model, not a design pattern.
– netcoder
Dec 2 '16 at 18:17
@TomSawyer OOP doesn't mean objects are required to have properties. What you're describing is a design pattern, one which is irrelevant to the question or an answer to that question. OOP is a language model, not a design pattern.
– netcoder
Dec 2 '16 at 18:17
add a comment |
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.)
8
link is invalid (404)
– Kyslik
Aug 6 '13 at 9:54
1
This works from WebArchive: web.archive.org/web/20101229204648/https://stackoverflow.com/…
– Tudor
Sep 19 '18 at 13:24
add a comment |
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.)
8
link is invalid (404)
– Kyslik
Aug 6 '13 at 9:54
1
This works from WebArchive: web.archive.org/web/20101229204648/https://stackoverflow.com/…
– Tudor
Sep 19 '18 at 13:24
add a comment |
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.)
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.)
edited May 23 '17 at 12:34
Community♦
11
11
answered May 3 '11 at 0:41
mariomario
126k17184256
126k17184256
8
link is invalid (404)
– Kyslik
Aug 6 '13 at 9:54
1
This works from WebArchive: web.archive.org/web/20101229204648/https://stackoverflow.com/…
– Tudor
Sep 19 '18 at 13:24
add a comment |
8
link is invalid (404)
– Kyslik
Aug 6 '13 at 9:54
1
This works from WebArchive: web.archive.org/web/20101229204648/https://stackoverflow.com/…
– Tudor
Sep 19 '18 at 13:24
8
8
link is invalid (404)
– Kyslik
Aug 6 '13 at 9:54
link is invalid (404)
– Kyslik
Aug 6 '13 at 9:54
1
1
This works from WebArchive: web.archive.org/web/20101229204648/https://stackoverflow.com/…
– Tudor
Sep 19 '18 at 13:24
This works from WebArchive: web.archive.org/web/20101229204648/https://stackoverflow.com/…
– Tudor
Sep 19 '18 at 13:24
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
edited Aug 19 '14 at 14:44
askmish
5,5091638
5,5091638
answered Jun 30 '14 at 13:09
feel good and programmingfeel good and programming
6302924
6302924
add a comment |
add a comment |
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.
12
"So if I had to change my database from MySQL to PostgreSQL there won't be any problem." Uhhhmmm with above code you would have a huge problem changing anything imo.
– PeeHaa
Oct 4 '12 at 19:54
I see my answer makes less and less sense after edit, and as time goes by. But it should stay here
– Ibu
Dec 19 '12 at 17:48
2
Database
in the example is not a class. It is just a wrapper for functions. Also, how can you have "table object class" without an object?
– tereško
Dec 19 '12 at 19:03
1
@tereško I have read many of your posts and they're great. But, I cannot find any complete framework anywhere to study. Do you know of one that "does it right"? Or at least one that does it like you and some others here on SO say to do? Thanks.
– johnny
Sep 1 '14 at 20:12
I may be way late, but i'd like to point out that PDO almost solves the issue of having to create a DB 'layer' in order to facilitate future changes.
– Matthew Goulart
Mar 3 '16 at 15:44
add a comment |
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.
12
"So if I had to change my database from MySQL to PostgreSQL there won't be any problem." Uhhhmmm with above code you would have a huge problem changing anything imo.
– PeeHaa
Oct 4 '12 at 19:54
I see my answer makes less and less sense after edit, and as time goes by. But it should stay here
– Ibu
Dec 19 '12 at 17:48
2
Database
in the example is not a class. It is just a wrapper for functions. Also, how can you have "table object class" without an object?
– tereško
Dec 19 '12 at 19:03
1
@tereško I have read many of your posts and they're great. But, I cannot find any complete framework anywhere to study. Do you know of one that "does it right"? Or at least one that does it like you and some others here on SO say to do? Thanks.
– johnny
Sep 1 '14 at 20:12
I may be way late, but i'd like to point out that PDO almost solves the issue of having to create a DB 'layer' in order to facilitate future changes.
– Matthew Goulart
Mar 3 '16 at 15:44
add a comment |
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.
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.
edited Jun 14 '12 at 20:24
Peter Mortensen
13.8k1987113
13.8k1987113
answered May 3 '11 at 0:47
IbuIbu
33.1k106188
33.1k106188
12
"So if I had to change my database from MySQL to PostgreSQL there won't be any problem." Uhhhmmm with above code you would have a huge problem changing anything imo.
– PeeHaa
Oct 4 '12 at 19:54
I see my answer makes less and less sense after edit, and as time goes by. But it should stay here
– Ibu
Dec 19 '12 at 17:48
2
Database
in the example is not a class. It is just a wrapper for functions. Also, how can you have "table object class" without an object?
– tereško
Dec 19 '12 at 19:03
1
@tereško I have read many of your posts and they're great. But, I cannot find any complete framework anywhere to study. Do you know of one that "does it right"? Or at least one that does it like you and some others here on SO say to do? Thanks.
– johnny
Sep 1 '14 at 20:12
I may be way late, but i'd like to point out that PDO almost solves the issue of having to create a DB 'layer' in order to facilitate future changes.
– Matthew Goulart
Mar 3 '16 at 15:44
add a comment |
12
"So if I had to change my database from MySQL to PostgreSQL there won't be any problem." Uhhhmmm with above code you would have a huge problem changing anything imo.
– PeeHaa
Oct 4 '12 at 19:54
I see my answer makes less and less sense after edit, and as time goes by. But it should stay here
– Ibu
Dec 19 '12 at 17:48
2
Database
in the example is not a class. It is just a wrapper for functions. Also, how can you have "table object class" without an object?
– tereško
Dec 19 '12 at 19:03
1
@tereško I have read many of your posts and they're great. But, I cannot find any complete framework anywhere to study. Do you know of one that "does it right"? Or at least one that does it like you and some others here on SO say to do? Thanks.
– johnny
Sep 1 '14 at 20:12
I may be way late, but i'd like to point out that PDO almost solves the issue of having to create a DB 'layer' in order to facilitate future changes.
– Matthew Goulart
Mar 3 '16 at 15:44
12
12
"So if I had to change my database from MySQL to PostgreSQL there won't be any problem." Uhhhmmm with above code you would have a huge problem changing anything imo.
– PeeHaa
Oct 4 '12 at 19:54
"So if I had to change my database from MySQL to PostgreSQL there won't be any problem." Uhhhmmm with above code you would have a huge problem changing anything imo.
– PeeHaa
Oct 4 '12 at 19:54
I see my answer makes less and less sense after edit, and as time goes by. But it should stay here
– Ibu
Dec 19 '12 at 17:48
I see my answer makes less and less sense after edit, and as time goes by. But it should stay here
– Ibu
Dec 19 '12 at 17:48
2
2
Database
in the example is not a class. It is just a wrapper for functions. Also, how can you have "table object class" without an object?– tereško
Dec 19 '12 at 19:03
Database
in the example is not a class. It is just a wrapper for functions. Also, how can you have "table object class" without an object?– tereško
Dec 19 '12 at 19:03
1
1
@tereško I have read many of your posts and they're great. But, I cannot find any complete framework anywhere to study. Do you know of one that "does it right"? Or at least one that does it like you and some others here on SO say to do? Thanks.
– johnny
Sep 1 '14 at 20:12
@tereško I have read many of your posts and they're great. But, I cannot find any complete framework anywhere to study. Do you know of one that "does it right"? Or at least one that does it like you and some others here on SO say to do? Thanks.
– johnny
Sep 1 '14 at 20:12
I may be way late, but i'd like to point out that PDO almost solves the issue of having to create a DB 'layer' in order to facilitate future changes.
– Matthew Goulart
Mar 3 '16 at 15:44
I may be way late, but i'd like to point out that PDO almost solves the issue of having to create a DB 'layer' in order to facilitate future changes.
– Matthew Goulart
Mar 3 '16 at 15:44
add a comment |
127
Why are you catching exceptions just to throw them again?
– Bailey Parker
Jun 8 '12 at 5:46
9
@Elias Van Ootegem: you missed the point. it's pointless to catch them in this case.
– Karoly Horvath
Jul 18 '12 at 20:46
4
@Elias Van Ootegem: huh? if it works with rethrow, it means that an upper layer catches the exception. But if there is one, then it would have catched it without that pointless rethrow... (if you still don't get it, please mock up a small test code)
– Karoly Horvath
Jul 18 '12 at 21:11
3
@Elias Van Ootegem: I have no idea what you're talking about, not handling an exception on a specific layer doesn't mean it will halt the app. please construct (or more precisely: fail to construct) a code example where that rethrow is necessary. let's stop this offtopic conversation, please
– Karoly Horvath
Jul 18 '12 at 22:01
6
@drrcknlsn: that's a valid argument, but in that case at least catch the exception you expect to be thrown, the generic
Exception
doesn't have much documentation value. Personally if I went down on that road I would choose PHPDoc's@exception
, or some similar mechanism, so it shows up in the generated documentation.– Karoly Horvath
Aug 2 '12 at 22:27