Let's say whenever I do a CRUD operation or modify a relationship in a specific way I also want to do something else. E.g., whenever someone publishes a post I also want to save something to a table for analytics. Maybe not the best example but in general there's a lot of this "grouped" functionality.
Normally I see this type of logic put into controllers. That's all fine an dandy until you want to reproduce this functionality in lots of places. When you start getting into partials, creating an API and generating dummy content it becomes an issue with keeping things DRY.
The ways I've seen to manage this are events, repositories, libraries, and adding to models. Here are my understandings of each:
Services: This is where most people would probably put this code. My main issue with services is that sometimes it's hard to find specific functionality in them and I feel like they get forgotten about when people are focused on using Eloquent. How would I know I need to call a method publishPost() in a library when I can just do $post->is_published = 1?
The only condition I see this working well in is if you ONLY use services (and ideally make Eloquent inaccessible somehow from controllers all together).
Ultimately it seems like this would just create a bunch of extra unnecessary files if your requests generally follow your model structure.
Repositories: From what I understand this is basically like a service but there's an interface so you can switch between ORMs, which I don't need.
Events: I see this as the most elegant system in a sense because you know your model events are always going to be called on Eloquent methods, so you can write your controllers like you normally would. I can see these getting messy though and if anyone has examples of large projects using events for critical coupling I'd like to see it.
Models: Traditionally I'd have classes that performed CRUD and also handled critical coupling. This actually made things easy because you knew all functionality around CRUD + whatever had to be done with it was there.
Simple, but in MVC architecture this isn't normally what I see done. In a sense though I prefer this over services since it's a bit easier to find, and there are less files to keep track of. It can get a bit disorganized though. I'd like to hear downfalls to this method and why most people don't seem to do it.
What are the advantages / disadvantages of each method? Am I missing something?
I think all patterns / architectures that you present are very useful as long as you follow the SOLID principles.
For the where to add logic I think that it's important to refer to the Single Responsibility Principle. Also, my answer considers that you are working on a medium / large project. If it's a throw-something-on-a-page project, forget this answer and add it all to controllers or models.
The short answer is: Where it makes sense to you (with services).
The long answer:
Controllers: What is the responsibility of Controllers? Sure, you can put all your logic in a controller, but is that the controller's responsibility? I don't think so.
For me, the controller must receive a request and return data and this is not the place to put validations, call db methods, etc..
Models: Is this a good place to add logic like sending an welcome email when a user registers or update the vote count of a post? What if you need to send the same email from another place in your code? Do you create a static method? What if that emails needs information from another model?
I think the model should represent an entity. With Laravel, I only use the model class to add things like fillable, guarded, table and the relations (this is because I use the Repository Pattern, otherwise the model would also have the save, update, find, etc methods).
Repositories (Repository Pattern): At the beginning I was very confused by this. And, like you, I thought "well, I use MySQL and thats that.".
However, I have balanced the pros vs cons of using the Repository Pattern and now I use it. I think that now, at this very moment, I will only need to use MySQL. But, if three years from now I need to change to something like MongoDB most of the work is done. All at the expense of one extra interface and a $app->bind(«interface», «repository»).
Events (Observer Pattern): Events are useful for things that can be thrown at any class any given time. Think, for instance, of sending notifications to a user.
When you need, you fire the event to send a notification at any class of your application. Then, you can have a class like UserNotificationEvents that handles all of your fired events for user notifications.
Services: Until now, you have the choice to add logic to controllers or models. For me, it makes all sense to add the logic within Services. Let's face it, Services is a fancy name for classes. And you can have as many classes as it makes sense to you within your aplication.
Take this example: A short while ago, I developed something like the Google Forms. I started with a CustomFormService and ended up with CustomFormService, CustomFormRender, CustomFieldService, CustomFieldRender, CustomAnswerService and CustomAnswerRender. Why? Because it made sense to me. If you work with a team, you should put your logic where it makes sense to the team.
The advantage of using Services vs Controllers / Models is that you are not constrained by a single Controller or a single Model. You can create as many services as needed based on the design and needs of your application. Add to that the advantage of calling a Service within any class of your application.
This goes long, but I would like to show you how I have structured my application:
app/
controllers/
MyCompany/
Composers/
Exceptions/
Models/
Observers/
Sanitizers/
ServiceProviders/
Services/
Validators/
views
(...)
I use each folder for a specific function. For example the Validators directory contains a BaseValidator class responsible for processing the validation, based on the $rules and $messages of specific validators (usually one for each model). I could as easily put this code within a Service, but it makes sense to me to have a specific folder for this even if it is only used within the service (for now).
I recommend you to read the following articles, as they might explain things a little better to you:
Breaking the Mold by Dayle Rees (author of CodeBright): This is where I put it all together, even though I changed a few things to fit my needs.
Decoupling your code in Laravel using Repositories and Services by Chris Goosey: This post explains well what is a Service and the Repository Pattern and how they fit together.
Laracasts also have the Repositories Simplified and Single Responsibility which are good resources with practical examples (even though you have to pay).
I wanted to post a response to my own question. I could talk about this for days, but I'm going to try to get this posted fast to make sure I get it up.
I ended up utilizing the existing structure that Laravel provides, meaning that I kept my files primarily as Model, View, and Controller. I also have a Libraries folder for reusable components that aren't really models.
I DID NOT WRAP MY MODELS IN SERVICES/LIBRARIES. All of the reasons provided didn't 100% convince me of the benefit of using services. While I may be wrong, as far as I can see they just result in tons of extra nearly empty files I need to create and switch between when working with models and also really reduce the benefit of using eloquent (especially when it comes to RETRIEVING models, e.g., using pagination, scopes, etc).
I put the business logic IN THE MODELS and access eloquent directly from my controllers. I use a number of approaches to make sure that the business logic doesn't get bypassed:
Accessors and mutators: Laravel has great accessors and mutators. If I want to perform an action whenever a post is moved from draft to published I can call this by creating function setIsPublishedAttribute and including the logic in there
Overriding Create/Update etc: You can always override Eloquent methods in your models to include custom functionality. That way you can call functionality on any CRUD operation. Edit: I think there's a bug with overriding create in newer Laravel versions (so I use events now registered in boot)
Validation: I hook my validation in the same way, e.g., I'll run validation by overriding CRUD functions and also accessors/mutators if needed. See Esensi or dwightwatson/validating for more information.
Magic Methods: I use the __get and __set methods of my models to hook into functionality where appropriate
Extending Eloquent: If there's an action you'd like to take on all update/create you can even extend eloquent and apply it to multiple models.
Events: This is a straight forward and generally agreed upon place to do this as well. Biggest drawback with events I think is that exceptions are hard to trace (might not be the new case with Laravel's new events system). I also like to group my events by what they do instead of when they are called...e.g., have a MailSender subscriber which listens for events that send mail.
Adding Pivot/BelongsToMany Events: One of the things I struggled with the longest was how to attach behavior to the modification of belongsToMany relationships. E.g., performing an action whenever a user joins a group. I'm almost done polishing up a custom library for this. I haven't published it yet but it is functional! Will try to post a link soon. EDIT I ended up making all my pivots into normal models and my life has been so much easier...
Addressing people's concerns with using models:
Organization: Yes if you include more logic in models, they can be longer, but in general I've found 75% of my models are still pretty small. If I chose to organize the larger ones I can do it using traits (e.g., create a folder for the model with some more files like PostScopes, PostAccessors, PostValidation, etc as needed). I know this is not necessarily what traits are for but this system works without issue.
Additional Note: I feel like wrapping your models in services is like having a swiss army knife, with lots of tools, and building another knife around it that basically does the same thing? Yeah, sometimes you might want to tape a blade off or make sure two blades are used together...but there are typically other ways to do it...
WHEN TO USE SERVICES: This article articulates very well GREAT examples for when to use services (hint: it's not very often). He says basically when your object uses multiple models or models at strange parts of their lifecycle it makes sense. http://www.justinweiss.com/articles/where-do-you-put-your-code/
What I use to do to create the logic between controllers and models is to create a service layer. Basically, this is my flow for any action within my app:
Controller get user's requested action and sent parameters and delegates everything to a service class.
Service class do all the logic related to the operation: input validation, event logging, database operations, etc...
Model holds information of fields, data transformation, and definitions of attributes validations.
This is how I do it:
This the method of a controller to create something:
public function processCreateCongregation()
{
// Get input data.
$congregation = new Congregation;
$congregation->name = Input::get('name');
$congregation->address = Input::get('address');
$congregation->pm_day_of_week = Input::get('pm_day_of_week');
$pmHours = Input::get('pm_datetime_hours');
$pmMinutes = Input::get('pm_datetime_minutes');
$congregation->pm_datetime = Carbon::createFromTime($pmHours, $pmMinutes, 0);
// Delegates actual operation to service.
try
{
CongregationService::createCongregation($congregation);
$this->success(trans('messages.congregationCreated'));
return Redirect::route('congregations.list');
}
catch (ValidationException $e)
{
// Catch validation errors thrown by service operation.
return Redirect::route('congregations.create')
->withInput(Input::all())
->withErrors($e->getValidator());
}
catch (Exception $e)
{
// Catch any unexpected exception.
return $this->unexpected($e);
}
}
This is the service class that does the logic related to the operation:
public static function createCongregation(Congregation $congregation)
{
// Log the operation.
Log::info('Create congregation.', compact('congregation'));
// Validate data.
$validator = $congregation->getValidator();
if ($validator->fails())
{
throw new ValidationException($validator);
}
// Save to the database.
$congregation->created_by = Auth::user()->id;
$congregation->updated_by = Auth::user()->id;
$congregation->save();
}
And this is my model:
class Congregation extends Eloquent
{
protected $table = 'congregations';
public function getValidator()
{
$data = array(
'name' => $this->name,
'address' => $this->address,
'pm_day_of_week' => $this->pm_day_of_week,
'pm_datetime' => $this->pm_datetime,
);
$rules = array(
'name' => ['required', 'unique:congregations'],
'address' => ['required'],
'pm_day_of_week' => ['required', 'integer', 'between:0,6'],
'pm_datetime' => ['required', 'regex:/([01]?[0-9]|2[0-3]):[0-5]?[0-9]:[0-5][0-9]/'],
);
return Validator::make($data, $rules);
}
public function getDates()
{
return array_merge_recursive(parent::getDates(), array(
'pm_datetime',
'cbs_datetime',
));
}
}
For more information about this way I use to organize my code for a Laravel app: https://github.com/rmariuzzo/Pitimi
In my opinion, Laravel already has many options for you to store your business logic.
Short answer:
Use Laravel's Request objects to automatically validate your input, and then persist the data in the request (create the model). Since all of the users input is directly available in the request, I believe it makes sense to perform this here.
Use Laravel's Job objects to perform tasks that require individual components, then simply dispatch them. I think Job's encompass service classes. They perform a task, such as business logic.
Long(er) answer:
Use Respositories When Required:
Repositories are bound to be over-bloated, and most of the time, are simply used as an accessor to the model. I feel like they definitely have some use, but unless you're developing a massive application that requires that amount of flexibility for you to be able to ditch Laravel entirely, stay away from repositories. You'll thank yourself later and your code will be much more straight forward.
Ask yourself if there's a possibility that you're going to be changing PHP frameworks or to a database type that Laravel doesn't support.
If your answer is "Probably not", then don't implement the repository pattern.
In addition to above, please don't slap a pattern on top of a superb ORM like Eloquent. You're just adding complexity that isn't required and it won't benefit you at all.
Utilize Services sparingly:
Service classes to me, are just a place to store business logic to perform a specific task with its given dependencies. Laravel has these out of the box, called 'Jobs', and they have much more flexibility than a custom Service class.
I feel like Laravel has a well-rounded solution for the MVC logic problem. It's just a matter or organization.
Example:
Request:
namespace App\Http\Requests;
use App\Post;
use App\Jobs\PostNotifier;
use App\Events\PostWasCreated;
use App\Http\Requests\Request;
class PostRequest extends Request
{
public function rules()
{
return [
'title' => 'required',
'description' => 'required'
];
}
public function persist(Post $post)
{
if (! $post->exists) {
// If the post doesn't exist, we'll assign the
// post as created by the current user.
$post->user_id = auth()->id();
}
$post->title = $this->title;
$post->description = $this->description;
$post->save();
// Maybe we'll fire an event here that we can catch somewhere
// else that needs to know when a post was created.
event(new PostWasCreated($post));
// Maybe we'll notify some users of the new post as well.
dispatch(new PostNotifier($post));
return $post;
}
}
Controller:
namespace App\Http\Controllers;
use App\Post;
use App\Http\Requests\PostRequest;
class PostController extends Controller
{
public function store(PostRequest $request)
{
$request->persist(new Post());
flash()->success('Successfully created new post!');
return redirect()->back();
}
public function update(PostRequest $request, Post $post)
{
$request->persist($post);
flash()->success('Successfully updated post!');
return redirect()->back();
}
}
In the example above, the request input is automatically validated, and all we need to do is call the persist method and pass in a new Post. I think readability and maintainability should always trump complex and unneeded design patterns.
You can then utilize the exact same persist method for updating posts as well, since we can check whether or not the post already exists and perform alternating logic when needed.
Related
Well i'm learning Symfony (3.3) and i'm little confused about Service Container. In first tutorial the lector show register, login, add post, edit, delete methods in User, Article Controllers. Then in other tutorial, they show same methods but use Service Container (User and Article services) with User and Article interfaces. So .. what is the best practice for implementation in Services instead of Controllers.
I would like to add to Alexandre's answer that it is considered best practice to keep your controller 'thin'. In other words, only put code in your controller that really has to be there (or if it only makes sense to put it in your controller).
Services are like tools you can use in your application. A service in an object that helps you do something. In one service, you can have many functions. I think the best way to understand the difference is that a controller is for one specific action, a service can be used in many actions. So if you have parts of code that you want to use in more than one controller, create a service for it. And for the sake of re-usability, create functions in your service that do only one thing. This makes it easier along the way to use these functions.
the "best practise" depends on what you want to do with the Service. If you build an REST-Api you might want to do Database-Operations in Controller. Why? When you rely on the SOLID-Pattern you want to reduce or eliminate redundant code. If you code a real REST Api you don't have redundant code because each REST-Verb will do a different query/thing.
So in a non-REST-Api-application you will have a lot of redundant code. You do the same things/services on different pages/controller-actions. So the best thing is to implement all the business-logic in services to have it only one time in one place. If you have a lot of individual queries place them into repositories. If you have business-logic that fit's into an entity-class place them there. So in my opinion you can choose a thick controller/no service design in API's and a thin controller/thick service design in classic symfony front-/backend applications.
But one more thing: there is no totally wrong way to design an application. But if you work with other people or want to run the application longer than a month (without having trouble to maintain it) you should pick a common design-pattern.
Controller must implement the application logic like check if it's a post request or if a form is submit etc...
Never use DQL or any SQL Request directly inside a controller !
EDIT In the example i use a find method inside the controller because it's a Repository method but i parse the result inside slugify (my service method)
Services contains business logic like formatting phone numbers, parse some data etc...
Of course you can inject a repository inside a service and call your methods inside it.
An example:
//This is a fictive example
public function indexAction(Request $request) {
//Application logic
if(!$request->get('id')) {
//redirect somewhere by example
}
$article = $this->getDoctrine()
->getRepository(Article::class)
->find($request->get('id'));
//Business Logic
$slug = $this->get('my.acme.service')->slugify($article->getTitle());
}
Hope this helps
I'm trying to understand the MVC pattern in Phalcon.
In my current application I only need ONE template file for each table. The template contains the datagrid, the SQL statement for the SELECT, the form, add/edit/delete-buttons, a search box and all things necessary to interact with the database, like connection information (of course using includes as much as possible to prevent duplicate code). (I wrote my own complex framework, which converts xml-templates into a complete HTML-page, including all generated Javascript-code and CSS, without any PHP needed for the business logic. Instead of having specific PHP classes for each table in the database, I only use standard operation-scripts and database-classes that can do everything). I'm trying to comply more with web standards though, so I'm investigating alternatives.
I tried the INVO example of Phalcon and noticed that the Companies-page needs a Companies model, a CompaniesController, a CompaniesForm and 4 different views. To me, compared to my single file template now, having so many different files is too confusing.
I agree that separating the presentation from the business logic makes sense, but I can't really understand why the model and controller need to be in separate classes. This only seems to make things more complicated. And it seems many people already are having trouble deciding what should be in the model and what should be in the controller anyway. For example validation sometimes is put in the model if it requires business logic, but otherwise in the controller, which seems quite complex.
I work in a small team only, so 'separation of concerns' (apart from the presentation and business logic) is not really the most important thing for us.
If I decide not to use separate model and controller classes,
what problems could I expect?
Phalcon's Phalcon\Mvc\Model class, which your models are supposed to extend, is designed to provide an object-oriented way of interacting with the database. For example, if your table is Shopping_Cart then you'd name your class ShoppingCart. If your table has a column "id" then you'd define a property in your class public $id;.
Phalcon also gives you methods like initialize() and beforeValidationOnCreate(). I will admit these methods can be very confusing regarding how they work and when they're ran and why you'd ever want to call it in the first place.
The initialize() is quite self-explanatory and is called whenever your class is initiated. Here you can do things like setSource if your table is named differently than your class or call methods like belongsTo and hasMany to define its relationship with other tables.
Relationship are useful since it makes it easy to do something like search for a product in a user's cart, then using the id, you'd get a reference to the Accounts table and finally grab the username of the seller of the item in the buyer's cart.
I mean, sure, you could do separate queries for this kind of stuff, but if you define the table relationships in the very beginning, why not?
In terms of what's the point of defining a dedicated model for each table in the database, you can define your own custom methods for managing the model. For example you might want to define a public function updateItemsInCart($productId,$quantity) method in your ShoppingCart class. Then the idea is whenever you need to interact with the ShoppingCart, you simply call this method and let the Model worry about the business logic. This is instead of writing some complex update query which would also work.
Yes, you can put this kind of stuff in your controller. But there's also a DRY (Don't Repeat Yourself) principle. The purpose of MVC is separation of concerns. So why follow MVC in the first place if you don't want a dedicated Models section? Well, perhaps you don't need one. Not every application requires a model. For example this code doesn't use any: https://github.com/phalcon/blog
Personally, after using Phalcon's Model structure for a while, I've started disliking their 1-tier approach to Models. I prefer multi-tier models more in the direction of entities, services, and repositories. You can find such code over here:
https://github.com/phalcon/mvc/tree/master/multiple-service-layer-model/apps/models
But such can become overkill very quickly and hard to manage due to using too much abstraction. A solution somewhere between the two is usually feasible.
But honestly, there's nothing wrong with using Phalcon's built-in database adapter for your queries. If you come across a query very difficult to write, nobody said that every one of your models needs to extend Phalcon\Mvc\Model. It's still perfectly sound logic to write something like:
$pdo = \Phalcon\DI::getDefault()->getDb()->prepare($sql);
foreach($params as $key => &$val)
{
$pdo->bindParam($key,$val);
}
$pdo->setFetchMode(PDO::FETCH_OBJ);
$pdo->execute();
$results=$pdo->fetchAll();
The models are very flexible, there's no "best" way to arrange them. The "whatever works" approach is fine. As well as the "I want my models to have a method for each operation I could possibly ever want".
I will admit that the invo and vokuro half-functional examples (built for demo purposes only) aren't so great for picking up good model designing habits. I'd advise finding a piece of software which is actually used in a serious manner, like the code for the forums: https://github.com/phalcon/forum/tree/master/app/models
Phalcon is still rather new of a framework to find good role models out there.
As you mention, regarding having all the models in one file, this is perfectly fine. Do note, as mentioned before, using setSource within initialize, you can name your classes differently than the table they're working on. You can also take advantage of namespaces and have the classes match the table names. You can take this a step further and create a single class for creating all your tables dynamically using setSource. That's assuming you want to use Phalcon's database adapter. There's nothing wrong with writing your own code on top of PDO or using another framework's database adapter out there.
As you say, separation of concerns isn't so important to you on a small team, so you can get away without a models directory. If it's any help, you could use something like what I wrote for your database adapter: http://pastie.org/10631358
then you'd toss that in your app/library directory. Load the component in your config like so:
$di->set('easySQL', function(){
return new EasySQL();
});
Then in your Basemodel you'd put:
public function easyQuery($sql,$params=array())
{
return $this->di->getEasySQL()->prepare($sql,$params)->execute()->fetchAll();
}
Finally, from a model, you can do something as simple as:
$this->easyQuery($sqlString,array(':id'=>$id));
Or define the function globally so your controllers can also use it, etc.
There's other ways to do it. Hopefully my "EasySQL" component brings you closer to your goal. Depending on your needs, maybe my "EasySQL" component is just the long way of writing:
$query = new \Phalcon\Mvc\Model\Query($sql, $di);
$matches=$query->execute($params);
If not, perhaps you're looking for something more in the direction of
$matches=MyModel::query()->where(...)->orderBy(...)->limit(...)->execute();
Which is perfectly fine.
Model, View and Controller were designed to separate each process.
Not just Phalcon uses this kind of approach, almost PHP Frameworks today uses that approach.
The Model should be the place where you're saving or updating things, it should not rely on other components but the database table itself (ONLY!), and you're just passing some boolean(if CRUD is done) or a database record query.
You could do that using your Controller, however if you'll be creating multiple controllers and you're doing the same process, it is much better to use 1 function from your model to call and to pass-in your data.
Also, Controllers supposed to be the script in the middle, it should be the one to dispatch every request, when saving records, when you need to use Model, if you need things to queue, you need to call some events, and lastly to respond using json response or showing your template adapter (volt).
We've shorten the word M-V-C, but in reality, we're processing these:
HTTP Request -> Services Loaded (including error handlers) -> The Router -> (Route Parser) -> (Dispatch to specified Controller) -> The Controller -> (Respond using JSON or Template Adapter | Call a Model | Call ACL | Call Event | Queue | API Request | etc....) -> end.
Background
This is a long and complicated question. I'm using php/MySQL as an example (since it's an actual example) but this could theoretically apply to other languages. Bear with me.
MVC Framework with no ORM
I'm creating an application that uses its own framework. For a variety of reasons, the following answers to this question will not be accepted:
Why don't you just use X framework?
Why don't you just use X ORM?
This application does its own queries (written by me) and that's how I'd like it to stay for now.
Business Logic?
"Business Logic" seems to be a meaningless buzzword, but I take it to essentially mean
The queries and the logic that builds the result set based on those queries
I've also read that the Model in an MVC should do all the business logic.
User.php is 884 lines
Now that I've gotten my app working fairly well, I'd like to refactor it so as not to have such abominations. User.php is essentially the model for Users (obviously). There are some responsibilities I see in it that I could easily pluck out, but a major hurdle I'm running into is:
How can I reconcile SOLID with MVC?
The reason that User.php has grown so large is because I do any query that requires a User member in that file. User is used for a ton of operations (it needs to do so much more than just CRUD), so any query that needs userid, username, etc. is run by a function in this file. Apparently the queries should be in the model (and not the controller), but I feel that this should definitely be split up somehow. I need to accomplish the following:
Create an API that covers all of these necessary queries in a compartmentalized way
Avoid giving access to the DB connection class when it's not necessary
Add User data to the view (User.php is doing that right now -- the view object is injected by a setter, which I think is also bad).
...so what I could do is create other objects like UserBranchManager, UserSiteManager, UserTagManager, etc. and each of those could have the relevant queries and the DB object injected to run those queries, but then how do they get the coveted User::$userid that they need to run these queries? Not only that, but how could I pass Branch::$branchid? Shouldn't those members be private? Adding a getter for them also makes that pointless.
I'm also not sure where to draw the line of how much an object should do. A lot of the operations similar but still different. A class for each one would be huge overkill.
Possible Answer
If I can't get any help, what I'll do (or at least try to do) is have a dependency injection container of some kind build dependencies for the objects above (e.g. UserBranchManager) and inject them into the relevant controller. These would have a DB and Query object. The Query object could be passed to low level models (like User) to bind parameters as needed, and the higher level models or whatever they are called would give the results back to the controller which would add the data to the template as needed as well. Some possible hurdles I see are creating proper contracts (e.g. the UserController should preferably depend on some abstraction of the user models) but some specifics are inevitably required, especially when it comes to the view.
Can anyone offer some wisdom in response to my rambling question?
Response to #tereško
He has provided a great answer not only here, but also at How should a model be structured in MVC?
Code
As requested, here is some extremely pared down code (basically services one request). Some important notes:
Right now, controllers are not classes, just files
The controller also handles a lot of the routing
There are no "view" objects, just the templates
This will probably look really bad
These are also things to improve, but I'm mostly worried about the model (User in particular since it's getting out of control):
#usr.php -- controller
$route = route();
$user = '';
$branch = '<TRUNK>';
if (count($route) > 0) {
if (count($route) > 1) {
list($user, $branch) = $route;
}
else {
list($user) = $route;
}
}
$dec = new Decorator('user');
$dec->css('user');
if (isset($_SESSION['user']) && $_SESSION['user']->is($user)) {
$usr = $_SESSION['user'];
}
else {
$usr = new User(new DB, $user);
}
$usr->setUpTemplate($dec, $branch);
return $dec->execute();
# User.php -- model
class User {
private $userid;
private $username;
private $db;
public function __construct(DB $db, $username = null) {
$this->username = $username;
$this->db = $DB;
}
public function is($user) {
return strtolower($this->username) === strtolower($user);
}
public function setUpTemplate(Decorator $dec, $branch) {
$dec->_title = "$this->username $branch";
// This function runs a moderately complicated query
// joining the branch name and this user id/name
$dec->branch = $this->getBranchDisplay($branch);
}
}
Questions about answers
Answer here:
You talk about leaving off caching/authentication/authorization. Are these important? Why aren't they covered? How do they relate to the model/controller/router?
The Data Mapper example has the Person class with methods like getExemption, isFlaggedForAudit .. what are those? It seems like those calculations would require DB data, so how does it get them? Person Mapper leaves off select. Isn't that important?
What is "domain logic?"
Answer 5863870 (specifically the code example):
Shouldn't these factory objects be abstractions (and not rely on creation via new) or are these special?
How would your API include the necessary definition files?
I've read a lot about how it's best for dependencies to be injected in the constructor (if they're mandatory). I assume you set the factories in this way, but why not the objects/mappers/services themselves? What about abstractions?
Are you worried about code duplication (e.g. most models requiring an _object_factory member in their class definition)? If so, how could you avoid this?
You're using protected. Why?
If you can provide any specific code examples that would be best since it's easier for me to pick stuff up that way.
I understand the theory of what your answers are saying and it's helped a lot. What I'm still interested in (and not totally sure of) is making sure that dependencies of objects in this API are handled in the best way (the worst would be new everywhere).
Dont confuse SOLID (You can get a good explanation of what it is on my blog at: http://crazycoders.net/2012/03/confoo-2012-make-your-project-solid/
SOLID is great when considering the framework that goes around the application you are trying to build. The management of the data itself is another thing. You can't really apply the Single Responsibility of the S of SOLID to a business model that RELIES on other business models such as User, Groups and Permissions.
Thus, you have to let some of the SOLID go when you build an application. What SOLID is good for, is to make sure your framework behind your application is strong.
For example, if you build your own framework and business model, you will probably have a base class MODEL another for DATABASEACCESS, just remember that your MODEL shouldn't be aware of how to get the data, just know that it must get data.
For example:
Good:
- MyApp_Models_User extends MyApp_Framework_Model
- MyApp_Models_Group extends MyApp_Framework_Model
- MyApp_Models_Permission extends MyApp_Framework_Model
- MyApp_Framework_Model
- MyApp_Framework_Provider
- MyApp_Framework_MysqliProvider extends MyApp_Framework_Provider
In this good part, you create a model like this:
$user = new MyApp_Models_User(new MyApp_Framework_MysqliProvider(...));
$user->load(1234);
This way, you will prevent a fail in the single responsibility, your provider is used to load the data from one of the many providers that exist and your model represents the data that you extracted, it doesn't know how to read or write the data, thats the providers job...
Bad way:
- MyApp_Model_User
- MyApp_Model_Group
- MyApp_Model_Permission
define('MyDB', 'localhost');
define('MyUser', 'user');
define('MyPass', 'pass');
$user = new MyApp_Models_User(1234);
Using this bad method you first of all break the single responsibility, your model represents something and also manages the input/ouput of the data. Also, you create a dependency by stating that you need to define constants for the model to connect to the database and you completly abstract the database methods, if you need to change them and have 37 models, you'll have TONS of work to do...
Now, you can, if you want work the bad way... i still do it, i'm aware of it, but sometimes, when you have crappy structure and want to refactor, you can and should work against a principle just to refactor correctly and slowly, THEN, refactor a little more and end up with something SOLID and MVC looking.
Just remember that SOLID doesn't apply to everything, it's just a guideline, but it's very very good guideline.
Well .. it depends on what is actually inside your ./user.php file. If i had to guess, you would be a situation, where your user "model" has too many responsibilities. Basically, you are violating single responsibility principle and not sure how to go about fixing that.
You did no provide any code .. so , lets continue with guessing ...
It is possible that your user "model" is implementing active record pattern. This would be the main source of SRP problems. You could watch this part of lecture slides. It will explain some of it. The point would be, instead of using active record, to go with something similar to a data mapper pattern.
Also, you might notice that some of the domain logic, which works with User class instances, seems to happen outside your "model". It might be beneficial to separate that part in a different structure. Otherwise you will be forcing the domain logic inside the controller. Maybe this comment could shine some light on the whole subject.
Another thing you might have crammed inside your user "model" could be parts of the authorization (no to confuse with authentication) mechanism. It could be pragmatic to separate this responsibility.
Update
You talk about leaving off caching/authentication/authorization. Are these important? Why aren't they covered? How do they relate to the model/controller/router?
Caching is something that you would add later in the application. The domain objects do not care where the data comes from. For that reason you can wither add the caching with in the service-level objects or inside the existing data mappers. I would advise to choose former option, because changing existing mappers might have unforeseen side effects. And because it would just over-complicate the existing mappers.
namespace Application\Service;
class Community{
public function getUserDetails( $uid )
{
$user = $this->domainObjectFactory->build('User');
$cache = $this->cacheFactory->build('User');
$user->setId( $uid );
try
{
$cache->pull( $user );
}
cache( NotFoundException $e)
{
$mapper = $this->mapperFactory->build('User');
$mapper->pull( $user );
$cache->push( $user );
}
return $user->getDetails();
}
}
This would illustrate a very simplified acquisition of user information based on user's ID. The code creates domain object and provides it with ID, then this $user ovject is used as condition to search for cached details or, if it fails, fetching that pulling that information from DB via the data mapper. Also, if that is successful, the details are pushed into the cache, for next time.
You might notice, that this example did not handle situation, when mapper cannot find such user with such ID in storage (usually - SQL database). As I said , it's a simplified example.
Also, you might notice, that this sort of caching can be easily added on case-by-case basis and would not drastically change how your logic behaves.
Authorization is another part, which should not directly influence your business logic. I already linked my preferred way for providing authentication. The idea is that, instead of checking for credentials inside controller (like here, here, here or here), the access rights are checked before you execute a method on the controller. This way you have additional options for handling the denial of access, without being stuck within a specific controller.
The Data Mapper example has the Person class with methods like getExemption(), isFlaggedForAudit() .. what are those? It seems like those calculations would require DB data, so how does it get them? Person Mapper leaves off select. Isn't that important?
The Person class is a domain object. It would contain the part of domain logic, that is associated directly with that entity.
For those methods to be executed, the mapper should at first load the data. In PHP it would look something like this:
$person = new Person;
$mapper = new PersonMapper( $databaseConnection );
$person->setId( $id );
$mapper->fetch( $person );
if ( $person->isFlaggedForAudit() )
{
return $person->getTaxableEearnings();
}
The names of methods in the PersonMapper are there as an example, so that you would understand, how the class is supposed to be used. I usually name methods fetch(), store() and remove() (or push/pull/remove ... depends on how much GIT have I been using). IMHO, there is no point to have a separate update() and insert() methods. If object's data was initially retrieved by mapper, then it is an UPDATE. If not - it is an INSERT. You can also determine it whether the value, which corresponds to PRIMARY KEY has been set or not (in some cases, at least).
What is "domain logic?"
It is part of the code which knows how to create an invoice and apply discount price for specific products. It's also the code which makes sure, that you do not submit registration form, you do not state that you have been born in 1592.
The MVC is made from two layers: presentation (can contain: views, templates, controllers) and model layer (can contain: services, domain objects, mappers). The presentation layer deals with user interaction and responses. The model layer deals with business and validation rules. You could say that domain business logic is everything in model, that does not deal with storage.
I guess there is no easy way to explain.
Shouldn't these factory objects be abstractions (and not rely on creation via new) or are these special?
Which "factory objects", where? Are you talking about this fragment ?
$serviceFactory = new ServiceFactory(
new DataMapperFactory( $dbhProvider ),
new DomainObjectFactory
);
$serviceFactory->setDefaultNamespace('Application\\Service');
That whole code fragment is supposed to be in bootstrap.php file (or you might be using index.php or init.php for that). It's the entry point for the application. It is not part of any class, therefore you cannot have *tight coupling" there. There is nothing to "couple with".
How would your API include the necessary definition files?
That fragment is not entire bootstrap.php file. Above that code are includes and initialization for autoloader. I am currently using dynamic loader, which lets classes from same namespace to be located in different folders.
Also, such autoloader is a development-stage artifact. In production code you have to use loader, which works with predefined hashtable and does not need to actually walk the tree of namespaces and locations.
I've read a lot about how it's best for dependencies to be injected in the constructor (if they're mandatory). I assume you set the factories in this way, but why not the objects/mappers/services themselves? What about abstractions?
What are you talking about ?!?
Are you worried about code duplication (e.g. most models requiring an _object_factory member in their class definition)? If so, how could you avoid this?
Did you actually LOOK at how old that code fragment in comments was ?!?!?
You're using protected. Why?
Because, if values/methods are defined with protected visibility, you can access them when you extend the original class. Also, that code example was made for old version of answer. Check the dates.
And no. I will not provide any specific code examples. Because each situation is different. If you want to do copy-paste development, then use CakePHP or CodeIgniter.
We are having trouble in deciding where to put the ->flush() call in a Symfony2 application. Let's see if you can "inspire" us, please.
Our application is very big. It currently has about 30 bundles. We have 2 separate developer teams: one does frontend (controllers + twigs) and another does core (database + services + model, etc).
Frontend is one project (has its own bundles, which do not have any doctrine models nor logic nor services, but have twigs, public images and css and controllers), and lives in one repository.
Core is another project (has its own bundles, which offer services, model objects, etc, has doctrine objects in their inside and have no controllers nor twigs), and lives in another repo.
The goal of this approach is that our product is delivered with DIFFERENT FRONTENDS (Core+Frontend1 for the web, Core+Frontend2 for the mobiles, Core+Frontend3 for the support-team with a special web to admin the normal users). So all "logic" is "in the core" and either one or other frontend project is consuming the same services, so an improvement in the Core, improves all the deploys without having to re-test every piece of frontend.
So... we are trying that the controllers NEVER access the doctrine objects, but acces a "modelling layer", so if ever the persistance layer changes, the controllers and twigs (ie: all the frontend) remains without a single change so we only have to re-test the core but not the frontend.
We are trying to make a MODEL in such a way that all access to DB in "encapsulated" so the controllers do NOT access the doctrine but to "services" that in turn use doctrine. Suppose we treat the objects "cars" and "people", then a controller can access a "cars_manager" service or a "people_manager" service from which to do ALL necessary operations (create objects, retrieve them, etc).
Where would you put the flush call?
Example (in pseudo-code, to make it simpler to read):
controller AjaxJsonAddDriverToCar( $CarId, $DriverId )
{
try
{
$Cars = getService( "core.cars_manager" );
$Car = $Cars->getCarById( $CarId );
$Car->addDriver( $DriverId );
$Result = JSON_OK;
}
catch
{
$Result = JSON_FAIL;
}
return $Result;
}
Provided that the controller does NOT know how the core is implemented... it should NOT get the doctrine and perform a ->flush() on it.
Inspiration is welcome.
Thanks.
To avoid calling flush from the controller, I suggest encapsulating all the code that updates the database for a particular controller action into a service method which calls flush() at the end, in which case flush() won't be called if the service method throws an exception.
In the example you have given this can be accomplished by replacing:
$Cars = getService( "core.cars_manager" );
$Car = $Cars->getCarById( $CarId );
$Car->addDriver( $DriverId );
$Result = JSON_OK;
with:
$Cars = getService( "core.cars_manager" );
$Cars->addDriverToCar($CarId, $DriverId);
$Result = JSON_OK;
and CarsManager::addDriverToCar would be something like:
$Car = $this->getCarById( $CarId );
$Car->addDriver( $DriverId );
$this->getEntityManager()->flush();
However, this is a fairly simplistic example as it only updates a single Entity and the beauty of flush is that it saves changes to all the entities you have added/removed/updated, constituting the completion of a unit of work.
The approach you described mentions managers which are entity specific. Whilst there is no reason that the manager for a complex entity can't have methods that create/update/remove multiple entities of various types it is worth considering the responsibilities of your manager classes. It may be helpful to have a manager for each entity type that handles simple Find and CRUD type operations for that entity and then an additional layer of managers between the entity managers and the controllers that handle the processing for a particular feature or set of features.
My first thought was some kind of active record, where you would tell the car to save itself. As Car is only boilerplate code, it could be ok that it knows about the database implementation and accesses some services.
My second thought was that the cars manager should know about the saving, so it would be something very similar to the entity manager and you woudl tell him flush and he flushes. You would basically abstract the entity manager and make him a bit easier to use (as there is no repository which one uses directly).
My third thought was wtf. I understand that you want to seperate the frontend from the backend. I don't understand why the frontend cannot operate on models but needs to operate on boilerplate code. The funny thing is: If the models change, so do your layers in between. If you don't want to change the layer, you could also not change the model (it's the same either way). E.g. you want to remove a field from the database: Remvoe the annotation and ignore it. No harm done. If you rename it, you can always have the old getter and setter in place, operating on the new name. And so on.
Of course I don't see the whole picture, but you may want to think this through again ;)
And here is another thought: Maybe you want to just tell the abstraction layer if the whole thing was a success or failure and he does everything what needs to be done (flushing the database, writing logs, sending emails and so on). If you can narrow your use cases down to success and failure and the service knows what to do, then this might be the easiest solution.
I want to add a user in users table via link like '/index/adduser/id/7' .
Question
Should I validate user input inside 'adduserAction' function inside controller or somewhere inside model file? I've put files containing database related functions inside 'models' directory. Suppose a user is added to a table via 'id'. This id is sent via 'get'. And finally its added to table by 'AddUser' function (inside model file). Then I should validate this 'id' inside 'adduserAction' or 'AddUser'.? Scalability-wise, would it be better to do it inside 'AddUser'?
There's a popular believe / paradigm that states:
Thin controllers, fat models.
What this means, is that your controller should only be responsible for doing the bare minimum to make sure actions change the state of models and serve the right view in return. With this in mind, the validation should occur in your models. But... hold on for a minute. Models aren't necceseraly 1 tiered layers.
I believe among .NET programmers the following setup (or something similar) is a pretty common practice (if the scale of the project validates it):
Controller
-> ServiceLayer
-> Repository
-> DataObject
And I'm starting too like this setup more and more. Moreover, I believe this setup is very doable in a Zend Framework environment too.
The term Model is somewhat of a vague term. In the above you could consider the three last layers as your Model layer. What the three layers represent is the following:
ServiceLayer:
Responsible for business logic. In other words: retrieving Dataobjects (Models) from the repository, tying Models together, AND validating Models before storing them, mailing a user, etc..
Repository:
Some type of persistence mechanism (like a Database), that serves DataObjects to the service layer, and stores DataObjects back in the persistence mechanism (after the service layer validated them)
DataObject:
You might consider this the actual Models. Preferably DataObjects have common interfaces (independant of the Repository that is), so that Repositories are interchangeable. In other words, when the repository changes from a database to an XML file from some webservice, the DataObject still has the same interface the service layer and ultimately the view can work with.
Hope this makes sense. This is basically my understanding of a more layered MVC setup right now. If anybody feels I have things mixed up please feel free to correct me.
Please keep in mind though, that not all projects dignify such a layered setup. In smaller projects you could perhaps do with just a 1 layered Model layer. In that case, validating should still be the Model's responsiblity. The controller should be thin (just for tying Model state and Views together through actions).
I would say put the validation in your model. You can then keep your validation rules in a central location. How should your controller know the exact length of a valid user name? That is model territory. Your controller can ask the model if a user name length is correct or not sure, but the rule itself needs to be in your model. In my controller I would do something like this:
$model = new Model;
$model->loadFromArray(something to get post);
if (!$model->isValid()) { forward back to form }
$model->save();
the ideal solution is to use validation inside your forms - i.e. appending it to your zend_form_elements - see http://framework.zend.com/manual/en/zend.form.elements.html
I'd do it in the Controller, not the Model. IMHO that's the better place because then the sanitized data is secure to use in the controller already. That's a good thing for comparing things, etc., even before the data is actually saved.
I know it's already answered, but I remember the "younger me" combing the web for some choice resources on sites like this and would like to share my research and what I do here. I do the following:
I use forms to do input validation
and filtering Do my validation inside
models
I have a validation method
which proxies the form validation
method.
This validation method can called upon
internally by model methods or externally
by the controller or view.
Here's a quick example:
//UserModel
class Default_Model_User
{
protected $_form;
public function getForm()
{
if(!isset($this->_form)) {
$this->_form = new Default_Model_Form_User();
}
}
public function validate($data)
{
if($result = $this->getForm()->isValid($data)) {
// if you have custom validation conditions outside of the form, then you
// can do the validation here also.
if($data['controller_entered'] == 'some value') {
$result = false;
}
}
return $result;
}
public function saveUser($data)
{
if($result = $this->validate($data)) {
// do something.
}
return $result;
}
}
If you haven't read it already, be sure to check out the Surviving The Deepend book freely available on the following URL:
http://www.survivethedeepend.com/zendframeworkbook/en/1.0
This chapter is specific to your dilema:
http://www.survivethedeepend.com/zendframeworkbook/en/1.0/implementing.the.domain.model.entries.and.authors#id1491270
You will no doubt run into other issues as you progress through... It may help to check out the comments on my blog post regarding the model layer where I cover this issue: http://www.rvdavid.net/my-zend-framework-model-layer-part-service-part-orm/
Hope this helps someone out.