Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 5.2 understanding "fat model, skinny controller"

I'm trying to understand how to use "fat model, skinny controller" in Laravel 5.2. Basically, I mostly understand the why, and the what, but not the how. I've been Googling for a while, and I have found several pages describing why (and some pages describing why not) and what, but no pages that makes it easy to understand how you create a fat model with skinny controllers.

I have created a extremely basic Todo-list, no login or validation, just the most basic todo-note functionality. This application basically uses "skinny model, fat controllers" and I want to rewrite the app so that it uses "fat model, skinny controllers" instead.

I have three tables in the MySQL-database:

  • users
    • id int(10)
    • uname varchar(255)
    • email varchar(255)
    • password varchar(60)
  • projects
    • id int(10)
    • pname varchar(255)
  • notes
    • id int(10)
    • user_id int(10)
    • project_id int(10)
    • content text
    • time_created timestamp
    • time_deadline timestamp
    • completed tinyint(1)
    • removed tinyint(1)

When I created the migrations for the tables, I used $table->foreign('user_id')->references('id')->on('users'); and $table->foreign('project_id')->references('id')->on('projects'); for the notes table migration. For some reason it did not work, so in the database notes.user_id and notes.project_id are not foreign keys to users.id and projects.id, which was the idea from the beginning. I'm guessing that it doesn't really matter for my questions below, but if it does, someone please tell me so I can try to fix that.

I have the following models (doc blocks removed)

app\User.php:

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

    public function notes()
    {
        return $this->hasMany(Note::class);
    }
}

app\Project.php:

<?php
namespace App;

use Illuminate\Database\Eloquent\Model;

class Project extends Model
{
    public function notes()
    {
        return $this->hasMany(Note::class);
    }
}

app\Note.php:

<?php
namespace App;

use Illuminate\Database\Eloquent\Model;

class Note extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function project()
    {
        return $this->belongsTo(Project::class);
    }
}

I have the following controllers (doc blocks removed)

app\Http\Controllers\UserController.php:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\User;
use Response;

class UserController extends Controller
{
    public function index()
    {
        try {
            $statusCode = 200;
            $users = User::orderBy('uname', 'asc')->get()->toArray();
            $response = [];

            foreach ($users as $user) {
                $this_row = array(
                    'id' => $user['id'],
                    'name' => $user['uname'],
                );

                $response[] = $this_row;
            }
        } catch (Exception $e) {
            $statusCode = 400;
        } finally {
            return Response::json($response, $statusCode);
        }
    }
}

app\Http\Controllers\ProjectController.php:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Project;
use Response;

class ProjectController extends Controller
{
    public function index()
    {
        try {
            $statusCode = 200;
            $projects = Project::orderBy('pname', 'asc')->get()->toArray();
            $response = [];

            foreach ($projects as $project) {
                $this_row = array(
                    'id' => $project['id'],
                    'name' => $project['pname'],
                );

                $response[] = $this_row;
            }
        } catch (Exception $e) {
            $statusCode = 400;
        } finally {
            return Response::json($response, $statusCode);
        }
    }
}

app\Http\Controllers\NoteController.php:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Note;
use App\User;
use App\Project;
use Input;
use Response;
use Redirect;

class NoteController extends Controller
{
    public function index()
    {
        try {
            $statusCode = 200;
            $notes = Note::where('removed', 0)
                ->orderBy('time_created', 'asc')->get()->toArray();
            $response = [];

            foreach ($notes as $note) {
                $user = User::find($note['user_id']); // Username for note
                $project = Project::find($note['project_id']); // Project name

                $this_row = array(
                    'id' => $note['id'],
                    'user' => $user['uname'],
                    'project' => $project['pname'],
                    'content' => $note['content'],
                    'completed' => $note['completed'],
                    'created' => $note['time_created'],
                    'deadline' => $note['time_deadline']
                );

                $response[] = $this_row;
            }
        } catch (Exception $e) {
            $statusCode = 400;
        } finally {
            return Response::json($response, $statusCode);
        }
    }

    public function destroy(Request $request)
    {
        try {
            $statusCode = 200;

            $note = Note::find($request->id);
            $note->removed = 1;
            $note->save();
        } catch (Exception $e) {
            $statusCode = 400;
        } finally {
            return $statusCode;
        }        
    }

    public function edit($request) 
    {
        try {
            $statusCode = 200;

            $note = Note::find($request);

            $response = array(
                'id' => $note['id'],
                'content' => $note['content'],
                'completed' => $note['completed'],
                'deadline' => $note['time_deadline']
            );
        } catch (Exception $e) {
            $statusCode = 400;
        } finally {
            return Response::json($response, $statusCode);
        }
    }

    public function update(Request $request)
    {
        try {
            $statusCode = 200;

            $note = Note::find($request->id);

            $note->content = $request->content;
            $note->time_deadline = $request->deadline;

            if ($request->completed == "true") {
                $note->completed = 1;
            } else {
                $note->completed = 0;
            }

            $note->save();
        } catch (Exception $e) {
            $statusCode = 400;
        } finally {
            return $statusCode;
        }
    }

    public function store(Request $request)
    {
        try {
            $statusCode = 200;

            $note = new Note;

            $note->user_id = $request->user;
            $note->project_id = $request->project;
            $note->content = $request->content;
            $note->time_deadline = $request->deadline;

            $note->save();
        } catch (Exception $e) {
            $statusCode = 400;
        } finally {
            return $statusCode;
        }    
    }
}

Finally, this is my app/Http/routes.php (comments removed)

<?php

Route::get('/', function () {
    return view('index');
});

Route::get('/notes', 'NoteController@index');
Route::get('/notes/{id}', 'NoteController@edit');
Route::delete('/notes', 'NoteController@destroy');
Route::put('/notes', 'NoteController@store');
Route::post('/notes', 'NoteController@update');

Route::get('/projects', 'ProjectController@index');
Route::get('/users', 'UserController@index');

Route::group(['middleware' => ['web']], function () {
    //
});

The complete code can be found at my GitHub here.

I'm using Angular to receive the JSON sent by the controllers. This works fine for my current page but as you can see, my controllers have a lot of logic, which I would like to move to the model. I don't understand how I do this, so here's my questions:

  • Which additional files should I create?
  • Where should they be located?
  • What do I need in those files except the logic that currently is in the controllers?
  • How should I rewrite the controllers to handle the data from the models?
like image 615
BluePrint Avatar asked Jan 12 '16 11:01

BluePrint


1 Answers

Your skinny controller could be the following, which will do the same what you did:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Project;

class ProjectController extends Controller
{
    public function index()
    {
        $projects = Project::orderBy('pname', 'asc')->get(['id', 'name']);
        return response()->make($projects);
    }
}

But as Fabio mentioned, if you want to go further, checkout repositories. Here is a good article: https://bosnadev.com/2015/03/07/using-repository-pattern-in-laravel-5/

In most cases I'm wrapping repositories into services to create the business logic. Controllers just handle routing, and models only contains relations or mutators and accessors. But it could differ by development methods.

Also, don't make db queries in foreach loops, take the advantage of Eloquent with, forexample:

$notes = Note::where('removed', 0)
            ->with(['user', 'project'])
            ->orderBy('time_created', 'asc')->get();

And you can access, like this:

foreach($notes as $note)
{
    echo $note->user->uname;
}
like image 129
Iamzozo Avatar answered Oct 04 '22 14:10

Iamzozo