5

I'm trying to get a more concrete understanding of MVC and keeping the controller layer as thin as possible.

One thing I keep asking myself is "Where should I call modelname->save()?"

Looking at the Laravel documentation, they set data to the model and call save in the controller which doesn't seem right...

<?php

namespace App\Http\Controllers;

use App\Flight;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class FlightController extends Controller
{
    public function store(Request $request)
    {
        // Validate the request...

        $flight = new Flight;

        $flight->name = $request->name;

        $flight->save();
    }
}

This is quite a simple example and might be why they do it all in the controller.

From my understanding and everything I've been reading, all business logic should sit inside the model, the controller is responsible for "traffic control" between the view and the model.

So would I be calling save inside the model itself? or should I be using a service layer?


Here is my current problem with example data.

I am updating the status of a model. The row already exists in the DB. I use PATCH /route/ to get to the controller method. From there I get the model.

class TimecardController extends Controller {
...
    public function markAsPass(Request $request, $id) {
        $test = Test::findOrFail($id);

        //I don't think this is the corect way  
        //$test->status = "passed";
        //$test->markedBy = "Teacher123";
        //$test->save();


        $test->passed();
        ...
        return redirect($redirect_url);
    }
}

class Test extends Model {
...
    public function passed() {
        $this->status = "passed";

        //would I call save here? 
        //$this->save();
    }
}

Do I take an approach like above? Or do I create a service layer where I would use the model instance to call the model functions and then call save on the model?

//in service class
public function makeTestAsPassed($test){
    $test->passed();
    $test->save();

}

Please let me know if any claification is needed.

SegFaultDev
  • 455
  • 1
  • 7
  • 24
  • 2
    Using a setter and calling save isn't business logic. The implementation of save is the business logic. – Devon Bessemer Feb 26 '18 at 20:47
  • This is a great question, but also one that is entirely unsuited for StackOverflow. There are lots of different opinions on the topic, and it's not really a question that has one specific correct answer. – Joel Hinz Feb 26 '18 at 20:48
  • @JoelHinz Where do you think I should post a question like this? I feel like there should be a general consensus on the proper way to do this. – SegFaultDev Feb 26 '18 at 20:56
  • @Devon so setting model values and saving in a controller method is an ok practice? – SegFaultDev Feb 26 '18 at 20:57
  • I think so. Some people may decide on a Repository pattern to not make your app dependent on Eloquent, but other than that, using save() or create() is what you're supposed to do from the controller, just not much more than that. – Devon Bessemer Feb 26 '18 at 21:26

2 Answers2

1

You’re right in that business logic belongs in models. If you take a “resourceful” approach to your applications (in that you create controllers around entities) then you’ll find that your controller actions seldom call more than one model method.

Instead of calling save(), you can call create() and update() methods on your model. In your store() controller action, you can create a new entity with one line like this:

public function store(CreateRequest $request)
{
    $model = Model::create($request->all());
}

And update an existing model in an update() action like this:

public function update(UpdateRequest $request, Model $model)
{
    $model->update($request->all());
}

When it comes to business logic, you can call other methods on your models, too. To use resourceful controllers, you don’t have to have a model that relates to a database table.

Take shipping an order. Most people would be tempted to put a ship() method in an OrderController, but what happens when you ship an order? What entity could shipping an order result in? Well, you’d be creating a shipment, so that could instead be a store() method on an OrderShipmentController. This store() method could then just call a ship() method on your Order model:

class OrderShipmentController extends Controller
{
    public function store(ShipOrderRequest $request, Order $order)
    {
        $order->ship();
    }
}

So as you can see, with resourceful controllers and route–model binding, you can have “skinny controllers” with your application’s business logic living in your models.

Martin Bean
  • 38,379
  • 25
  • 128
  • 201
0

MVC is designed for ease of maintenance. The approach you don't recognize as "good" is the proper approach. All data handling related to business logic goes in the controller. Otherwise, a different coder will be confused as he/she would not find the data manipulation logic in the controller code.

Your thin controller goal defeats MVC.

Also note the model code is purposed to be thin as it is a place to define the database schema as mirror image to the database tables.

MVC is not object oriented abstration. MVC is a structure for code maintenance uniformity.

  • The model is where business logic belongs in MVC. Controllers are the transport layer An HTTP Controller should take an HTTP request, pass the data to, or from, the model, and then return an HTTP response. – Devon Bessemer Feb 26 '18 at 21:25
  • _Also note the model code is purposed to be thin as it is a place to define the database schema as mirror image to the database tables._ Nope. A model is meant to _model_ an object, and _objects_ do _things_. Those _things_ an object does is its business logic, and should live in methods on your model classes. – Martin Bean Feb 27 '18 at 14:55