4

I'm currently rebuilding my vanilla-PHP-App with Laravel and I have the following problem. I have multiple database-tables, that represent word categories (noun, verb, adverb, ...). For each table I created a separate Model, a route::resource and a separate resource-Controller. For example:

NomenController.php

public function show($id)
{
    $vocab = Nomen::find($id);
    return view('glossarium.vocab_update', compact('vocab'));
}

and

VerbController.php

public function show($id)
{
    $vocab = Verb::find($id);
    return view('glossarium.vocab_update', compact('vocab'));
}

...which are essentially the same except the Model class.

I don't want to create a separate Controller for each model, that does exactly the same. What would be the most simple and elegant way to solve this? Should I just create a VocabController.php and add a parameter for the Model-name like:

Route::resource('/vocab/{category}', 'VocabController');

and then add a constructor method in this controller like

public function __construct ($category) {
    if ($category == 'nomen') {
        $this->vocab = App\Nomen;
    }
    else if ($category == 'verb') {
         $this->vocab = App\Verb;
    }
}

I wonder if there is a simpler method to do that. Can I somehow do this with Route Model Binding?

Thanks in advance

1
  • You suggested way would work, but I would suggest creating a "Vocab" model that nouns/verbs/etc inherit from, then you just need your vocab controller. Commented Dec 2, 2016 at 19:15

2 Answers 2

7

Simply create a trait like this in App\Traits, (you can name it anything... Don't go with mine though... I feel its pretty lame... :P)

namespace App\Traits;

trait CommonControllerFunctions {

    public function show($id) {
        $modelObject = $this->model;

        $model = $modelObject::find($id);

        return view('glossarium.vocab_update', compact('model'));
    }

}

and in your NomenController and VerbController, do this:

use App\Traits\CommonControllerFunctions;

class NomenController {

    use CommonControllerFunctions;

    protected $model = Nomen::class;

}

and

use App\Traits\CommonControllerFunctions;

class VerbController {

    use CommonControllerFunctions;

    protected $model = Verb::class;

}

Note: Please note that this example is just a work-around for your particular situation only... Everyone practices code differently, so this method might not be approved by all...

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you! I will read a bit more about traits, then try it out and post about it.
This solution still doesn't solve problem with router. Developer still have to create 2 routes. Using traits for two identical controllers isn't the easiest solution ;-)
@piotr Actually, each resource route should work for one single controller only. Putting everything into one controller is a wrong practice. If one day a separate field is added to say 'Verbs' table... Then a new controller will have to be made along with a new route (anyways) and all steps will have to be repeated... Therefore, this is an invalid practice of structuring code and I myself don't approve of such coding practices that is why I wrote the last line in my answer. Each function in our code must follow single responsibility rule.
You are right, but Bene wanted as simple as possible solution
@piotr Simple it can be. Invalid it can not. I have no problems with your answer. I just shared what I feel is valid/authentic according to me. Personally, I would never follow the approach of Bene. This is not how we follow a modular structure. Every Controller must have its own view, own modal. Coding should be done in such a way that adding anything extra in the future doesn't lead to breaking changes in the code.
2

I think the simpliest way it to create only one controller, eg VocabController with methods nomen, verb and whatever you want.

Routes:

Route::get('/vocab/nomen/{nomen}', 'VocabController@item');
Route::get('/vocab/verb/{verb}', 'VocabController@item');

And the model binding:

Route::model('nomen', 'App\Nomen');
Route::model('verb', 'App\Varb');

Then your method shoud look like that:

public function item($item)
{
    return view('glossarium.vocab_update', $item);
}

Keep in mind, that $item is already fetched model from the database.

2 Comments

Hmm looks interesting. If I have a Controller method for each database table, would I then need separate methods for the CRUD-Operations like public function verb_create,.... public function verb_delete, nomen_create, nomen_delete, etc... ? Or do I just have to write different routes for each request (like PUT, DELETE, etc...) ?
I think that the separate routes are better

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.