I'm asking myself if i'm doing things in the right way. Here is my concern :
I have my User Model like this
class User extends Model {
public function activities()
{
return $this->hasMany('App\Activity');
}
....
public function getTotalDistanceTraveled() {
return Step::join('activities', 'activity_id', '=', 'activities.id')->where('activities.user_id', $this->id)->sum('steps.km');
}
Is it the right place to put function like getTotalDistanceTraveled() in the User Model ? Or it should be in the Controller which generate the View ? Or do I have to create a UserRepository ?
Thanks for your recommandations
For me it depends on the size of the app. If it's a simple app that is going to take very little maintenance and basic Eloquent queries then sure keep them in the model - what's the point in troubling yourself over a small app?
If your app is going to be constantly maintained and large in size then I would result to a Repository design, that way you keep everything organised, maintenance is easier and anybody else working on the project with yourself can find their way around relatively easier.
Personally on a large app I like to have repositories but split my more advanced, complex queries into a dedicated class for each query and remove them from the repository. This for me makes maintaining easier.
Controller wise, I personally prefer to keep my controllers 'skinny'. But a simple Eloquent query isn't going to hurt anything for example:
public function view($id)
{
return view('view-user')->with('user', User::find($id))
}
It ultimately depends on the scope of your app.
Related
I'm trying to adopt SOLID principles in my app.
Say I have these 2 models:
Client (fields=id, name, address etc) which hasMany:
Holdings (fields=id, client_id, ticker, holding_date, value)
In my ClientsController I might have a method like so:
public function show($id)
{
$client = Client::find($id);
$client->setValuations();
$valuations = $client->getValuations();
return View::make('clients.show')-with(compact('client', 'valuations'));
}
So in the controller I want to get the valuations over time for the client. The setValuations() on my client model performs a fairly complex query which sums the holdings for a client and sets the resulant collection as a property on the client.
So my client model might look a bit like:
class Client extends \Eloquent {
// All the usual model stuff
protected $valuations;
public function setValuations()
{
$this->valuations = DB::table('holdings')
->select('holdings.holding_date', DB::raw('SUM(holdings.value) AS sumofvalue') )
->where('holdings.client_id', $this->id)
->where('holdings.holding_date', DB::raw('LAST_DAY(holdings.holding_date)') )
->groupBy('holdings.holding_date')
->orderBy('holdings.holding_date', 'asc')
->get();
return $this;
}
public function getValuations()
{
return $this->valuations;
}
}
As we can see this is a fair amount of crap to put in a model (and I have condensed it a lot for the sake of brevity!). I should think that using a repository pattern might be the best approach but I'm unsure of how to structure this. Assuming I have several client-related attributes that need a fair amount of querying or processing to determine what they are (such as returns, transactions in client currency - this would require applying fx conversion to a collection of values, for example), how would be the best way to structure this and where should the logic be placed?
You are absolutely right. Keeping logic like this bloats the model and leads to violations of SOLID principles. I use the repository pattern to move this kind of logic out of my model (who should not have that responsibility i.e. Single Responsibility Principle) and into a dedicated class. I suggest you take a look at an open source project like the Laravel.io website that utilizes the repository pattern. After seeing their code you should be able to apply the pattern for yourself. Here is a link https://github.com/LaravelIO/laravel.io
I also highly recommend https://www.laracasts.com . SOLID principles and the repository pattern are thoroughly discussed and has drastically improved my understanding of Laravel and coding best practices.
I am no expert in the MVC architecture, however I have been working using the mentioned architecture on some JAVA projects. Now I'm getting into PHP MVC Frameworks, (like CakePHP and Laravel) and I decided to go for CakePHP.
Going deep into it, I see Models in CakePHP are in charge of operations like querying and some others while data itself is managed in arrays (i.e. $user['User']['first_name']).
The way I am familiar with, Model classes are just for storing model data, in other words, what arrays do in CakePHP
public Class User {
private String firstName;
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getFirstName() { return this.firstName; }
}
And Controller classes are in charge of querying and other model-building operation, in other words, what models do in CakePHP:
public Class UserManager {
public User getById(int id) { /* */ }
public boolean save(User user) { /* */ }
}
So, are CakePHP, and simillar MVC Frameworks, Models actually controllers? also if yes, shouldn't this be a new type of architecture, something like Router-View-Controller, since controller class actually routes request ?
I think the official documentation explains it already pretty well:
Models are the classes that sit as the business layer in your
application. This means that they should be responsible for managing
almost everything that happens regarding your data, its validity,
interactions and evolution of the information workflow in your domain
of work.
Usually model classes represent data and are used in CakePHP
applications for data access, more specifically they represent a
database table but they are not limited to this, but can be used to
access anything that manipulates data such as files, external web
services, iCal events, or rows in a CSV file.
Actually a model in CakePHP 2.0 is basically thought to do any kind of data manipulation.
Need to turn an array into a CSV string? Model job.
Need to validate an uploaded image? Model job.
Need to save an uploaded file? Model job.
Need to somehow manipulate your POST data? Model job.
...
None of the tasks above is thought to be done in a controller, we want to respect separation of concerns (SoC). The controller just manages the request but doesn't process data, that's the models job. Also models are more easy to unit test than controllers plus you can share them in a shell, you cant do that with controllers without MVC violations: Here is an example:
You should always keep your models fat and controllers thin:
/** CONTROLLER **/
if ($this->request->is('post')) {
if ($this->Model->doSomething($this->request->data)) {
$this->Session->setFlash(__('Something was done right!'));
$this->redirect(['view' => $this->Model->data['Model']['id']);
}
}
/** MODEL **/
public function doSomething($data) {
$this->create();
$result = $this->save($data);
if ($result) {
$result[$this->alias][$this->primaryKey] = $this->getLastInsertId();
$this->data = $result;
return true;
}
return false;
}
No, models are not controllers, not in any php framework out there.
Models just present a collection of your data, a controller at the other hand controls the flow between models and view. For example, the controller calls your model to get data, then process that somehow and push to to the view, so you can pick it up there and do something with it.
Are you willing to tell why you choosed for cakePHP ?
let's take two example.
Example 1 (Repository pattern)
Interface
interface FooInterface {
public function all();
}
Model(Using it in a loose term)
class FooModel implements FooInterface {
public function all()
{
return DB::('sometable')->get();
}
}
Service Provider
class FooServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind(
'Foo\FooInterface',
'Foo\FooModel'
);
}
config/app.php
'providers' => array(
// --
'Foo\FooServiceProvider'
),
And at last the controller:
use Foo\FooInterface as Model;
public function __construct(Model $model)
{
$this->model = $model;
}
Now i can access the methods as $this->model->all(). that's great! Let's look at the 2nd example.
Example 2:
controller:
public function __construct()
{
$this->model = new \Foo\FooModel();
}
now i can also access the same method as $this->model->all();
Question
As i read, the advantage of using repository pattern is, in future, easily configurable/changeable interface system. e.g.
if i change the db system, i just need to change, the bindings in the service provider.
But, i can also just easily change the model instaintiation in the controller construct to achieve the same. like, changing $this->model = new \Foo\FooModel() to $this->model = new \Bar\BarModel(); where BarModel will hold the methods of different system.
What exactly i am missing here in the aspect of advantages of a repository pattern.? or in this particular case, repository pattern doesn't give much advantage yet, in some other case it may? if that's a yes, what can be that situation?
p.s. the term model is used just for the convenince.
Being prepared for switching databases is one of the most annoying arguments one can give for the repository pattern. This one is usually followed by the ignorant "I'll never switch databases." If i had an upvote for each time I had this conversation...
Now imagine you want to add a back-end like caching or some search engine to optimize searches. This would fit really well in a repository pattern.
The general key benefit of the repository pattern is that all changes related to the back-end are more manageable than otherwise.
To demonstrate why you wouldn't want this stuff in a model; If you want to migrate a model's attributes the model needs to be fetched from the back-end differently. You might need two models in some cases. When you have this all in one model you need to apply a lot of hackery to make this even work. If the repository manages these changes the models stay clear and your code becomes manageable again.
It really comes down to how you have set-up your code. In your particular case, there wouldn't appear to be much benefit.
But what if your code required you to have multiple instantiations of your model across many different controllers? For example, maybe you have a model to your user repository. There may be many controllers which need to get information about the user.
Then it would be a hassle to go through all your controllers changing all the references (i.e. your example 2). Much better to just change the repository once (i.e. your example 1).
There is never one size fits all in coding. The best you can do is code for what you need now, with an awareness of any potential solutions which may aid flexibility in the future. My view is that the repository pattern is one of those solutions which aids flexibility. You may never need to change the model, or move to a different database, but the effort in coding with it now is minimal compared to the hassle you will have if you ever did want to change your db.
i've some questions on controllers structure for limiting duplicating code.
for example i want to retrieve men and woman. What's the best method to do this:
class User {
public function men() {
//render
}
public function women() {
//render
}
//OR
public function by_type($type) {
//render
}
}
It's a simple example but the number of type can grow. And each type can have seperate views. I'm searching for a scaling solution for the future. A best practice for this case of use.
Thanks
Rails has a (somewhat controversial) principle called fat model, skinny controller, which basically means that you can use the controller to process logic for the views, and let the models handle the "heavy lifting" so-to-speak
CakePHP / Rails
To port from CakePHP to Rails, I would highly recommend looking at using the models as much as possible, as it allows you to create an application which utilizes the full performance structure of the server, and not just leave all the logic in the controllers, as is what many people do with CakePHP
Specifically for your issue:
#app/controllers/users_controller.rb
def index
#user = User.gender(params[:gender])
end
#app/models/user.rb
def self.gender(type)
where?("type = ?", type)
end
This allows you to keep your controller as thin as possible, thus allowing for the correct distribution of code throughout the application
I see Rails as a lot different than CakePHP, in that it helps you create really functional & content-rich applications that utilize the entire server, rather than just providing a layer to make a website dynamic
As far as I understand your question, you could call the function that really render the type inside the by_type function this way:
public function by_type($type) {
if (method_exist($this, $type) {
return call_user_func(array($this, $type));
}
else {
throw new Exception('method do not exists!');
}
}
This way you only need to write the method that render the type and call it using by_type method.
I am trying to follow good practices as much as possible while I'm learning using OOP in an MVC structure, so i'm turning to you guys for a bit of advice on something which is bothering me a little here.
I am writing a site where I will have a number of different forms for members to fill in (mainly data about themselves), so i've decided to set up a Member controller where all of the forms relating to the member are represented as individual methods. This includes login/logout methods, as well as editing profile data etc. In addition to these methods, i also have a method to generate the member's control panel widget, which is a constant on every page on the site while the member is logged in. The only thing is, all of the other methods in this controller all have the same dependencies and form templates, so it would be great to generate all this in the constructor, but as the control_panel method does not have the same dependencies etc, I cannot use the constructor for this purpose, and instead I have to redeclare the dependencies and same template snippets in each method. This obviously isn't ideal and doesn't follow DRY principle, but I'm wondering what I should do with the control_panel method, as it is related to the member and that's why I put it in that controller in the first place.
Am I just over-complicating things here and does it make sense to just move the control_panel method into a simple helper class?
Here are the basic methods of the controller:
class Member_Controller extends Website_Controller {
public function __construct()
{
parent::__construct();
if (request::is_ajax())
{
$this->auto_render = FALSE; // disable auto render
}
}
public static function control_panel()
{
//load control panel view
$panel = new View('user/control_panel');
return $panel;
}
public function login()
{
}
public function register()
{
}
public function profile()
{
}
public function household()
{
}
public function edit_profile()
{
}
public function logout()
{
}
}
What I would do, is to avoid shoving everything in one controller, and instead separate functionality accordingly - for example, you could have a Registration_Controller to deal only with members' registration, Authentication_Controller, Profile_Controller, and so on, this way is easier to visualize what each part of your application is responsible for, instead of having one single controller with lots of responsibilities, which leads to confusion and other maintainability issues, at least IMHO, it has worked for me.
Going back to your concrete question about the control panel, yes it makes more sense to take it out of the controller, specially if is not an action of it, and as you mentioned you can have a helper class for all the repeatable display logic. If something doesn't make sense within the context of the controller, take it out.
There are some cool frameworks such as Cake and Zend FW, that make life easier at designing/developing MVC application, and that come with a rich set of components that work out-of-the-box.
MVC and Fw's are not mandatory though, it all comes to the scope and your special needs, some times they are of great deal help, most of the times I use them, but sometimes overcomplicate things. Keep it simple ;)
Cheers,
M.