Laravel 5.2.3 repeated part of code in several controllers/methods - php

I don't know if it's good practice. But I think that I can put this part of code:
$categories = DB::table('categories')
//[something]
->get();
somewhere to not ctrl+c ctrl+v in many places. Can you tell me what can I do with it in Laravel?
full example:
class FirstController extends Controller
{
public function index()
{
$articles = DB::table('articles')
//[something]
->get();
$categories = DB::table('categories')
//[something]
->get();
return view('pages.home', compact('articles', 'categories'));
}
public function show($id)
{
$categories = DB::table('categories')
//[something]
->get();
$article = Article::findOrFail($id);
return view('pages.show', compact('article', 'categories'));
}
}
class SecondController extends Controller
{
public function index()
{
$categories = DB::table('categories')
//[something]
->get();
return view('pages.contact')->with('categories', $categories);
}
}

What you might consider is writing a separate Repository (and Service) for every group of database interactions with method names that accurately describe what is going on and then using Laravels Dependency Injection framework to wire it to your controllers. This is a good resource on how to do that. This also looks really promising. This is the recommended approach if you anticipate that this project will become larger and should remain maintainable (and if your time and resources allow it).
You should make the consideration if what you're currently doing is "good enough" or if it would become unmaintainable in the future and change the implementation to using repositories (and possible services).
After studying your code a bit, a CategoryRepository would look something like this:
use Illuminate\Database\ConnectionInterface;
class CategoryRepository {
protected $connectionInterface;
public function __construct(ConnectionInterface $_connectionInterface) {
$this->connectionInterface = $_connectionInterface;
}
public function all() {
return db::table('categories')
//[something]
->get();
}
}
which you can then reference and use in your controllers like so:
class FirstController extends Controller {
protected $categoryRepository;
public function __construct(CategoryRepository $_categoryRepository) {
$this->categoryRepository = $_categoryRepository;
}
...
public function show($id) {
$categories = $this->categoryRepository->all();
$article = Article::findOrFail($id);
return view('pages.show', compact('article', 'categories'));
}
...
}
You could then try to write an get method, a save method etc. After that, you could write an ArticleRepository and incrementally clean up your controller.
I haven't verified this code so copy-and-paste with caution.

What you need is called View Composer in Laravel.
View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view each time that view is rendered, a view composer can help you organize that logic into a single location.
More about View Composers: https://laravel.com/docs/5.2/views#view-composers

Related

Laravel: One Controller for multiple Models

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
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...
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.

Laravel Object Oriented

I am somewhat new to OOP, although I know about interfaces and abstract classes a bit. I have a lot of resource controllers that are somewhat similar in the bigger scheme of things, they all look like the example below, the only main difference is the index and what I pass to the index view.
What I simply need to know is, can I OO things up a bit with my resource controllers? For example, create one "main" resource controller in which I simply pass the correct instances using an interface for example? I tried playing around with this but I got an error that the interface wasn't instantiable, so I had to bind it. But that means I could only bind an interface to a specific controller.
Any advice, tips and pointers will help me out :)
class NotesController extends Controller
{
public function index()
{
$notes = Note::all();
return view('notes.index', compact('notes'));
}
public function create()
{
return view('notes.create');
}
public function show(Note $note)
{
return view('notes.show', compact('note'));
}
public function edit(Note $note)
{
return view('notes.edit', compact('note'));
}
public function store(Request $request, User $user)
{
$user->getNotes()->create($request->all());
flash()->success('The note has been stored in the database.', 'Note created.');
return Redirect::route('notes.index');
}
public function update(Note $note, Request $request)
{
$note->update($request->all());
flash()->success('The note has been successfully edited.', 'Note edited.');
return Redirect::route('notes.index');
}
public function delete($slug)
{
Note::where('slug', '=', $slug)->delete();
return Redirect::to('notes');
}
}
Note: Totally my opinion!
I would keep them how you have them. It makes them easier to read and understand later. Also will save you time when you need to update one to do something different from the rest. We tried this in a project I worked on and while granted it wasn't the best implementation, it is still a pain point to this day.
Up to you though. I'm sure people have done that in a way that they love and works great. Just hasn't been the case in my experience. I doubt anyone would look at your code though and criticize you for not doing it.
In Case you need to bind different Model instanses then you may use Contextual Binding, for example, put the following code in AppServiceProvider's register() method:
$this->app->when('App\Http\Controllers\MainController')
->needs('Illuminate\Database\Eloquent\Model')
->give(function () {
$path = $this->app->request->path();
$resource = trim($path, '/');
if($pos = strpos($path, '/')) {
$resource = substr($path, 0, $pos);
}
$modelName = studly_case(str_singular($resource));
return app('App\\'.$modelName); // return the appropriate model
});
In your controller, use a __construct method to inject the model like this:
// Put the following at top of the class: use Illuminate\Database\Eloquent\Model;
public function __construct(Model $model)
{
$this->model = $model;
}
Then you may use something like this:
public function index()
{
// Extract this code in a separate method
$array = explode('\\', get_class($this->model));
$view = strtolower(end($array));
// Load the result
$result = $this->model->all();
return view($view.'.index', compact('result'));
}
Hope you got the idea so implement the rest of the methods.

What is the best way to filter collection in Laravel?

I am thinking a best solution at the same time a lazy solution to filter collection in eloquent result in Laravel. I want to filter all my $videos collection in all my controllers. Is that possible to do without rewriting the controllers and instead put it in the model?
Here is my filter code:
$videos = $videos->filter(function( $video ){
return $video->isPublished();
});
Use query scopes. You can learn from here. And in your case, it would be something like this:
class Video extends Eloquent {
public function scopePublished($query)
{
return $query->where('published', '1');
}
}
class VideosController extends BaseController {
public function showPublishedVideos()
{
return View::make('published_videos')
->with('videos', Video::published()->take(10)->get());
}
}

Laravel Dependency Injection: When do you have to? When can you mock Facades? Advantages of either method?

I've been using Laravel for a while now and I have been reading a lot about Dependency Injection an testable code. I've come to a point of confusion when talking about Facades and Mocked Objects. I see two patterns:
class Post extends Eloquent {
protected $guarded = array();
public static $rules = array();
}
This is my Post Model. I could run Post::all(); to get all the posts from my blog. Now I want to incorporate it into my controller.
Option #1: Dependency Injection
My first instinct would be to inject the Post model as a dependecy:
class HomeController extends BaseController {
public function __construct(Post $post)
{
$this->post = $post;
}
public function index()
{
$posts = $this->posts->all();
return View::make( 'posts' , compact( $posts );
}
}
My unit test would look like this:
<?php
use \Mockery;
class HomeControllerTest extends TestCase {
public function tearDown()
{
Mockery::close();
parent::tearDown();
}
public function testIndex()
{
$post_collection = new StdClass();
$post = Mockery::mock('Eloquent', 'Post')
->shouldRecieve('all')
->once()
->andReturn($post_collection);
$this->app->instance('Post',$post);
$this->client->request('GET', 'posts');
$this->assertViewHas('posts');
}
}
Option #2: Facade Mocks
class HomeController extends BaseController {
public function index()
{
$posts = Post::all();
return View::make( 'posts' , compact( $posts );
}
}
My unit test would look like this:
<?php
use \Mockery;
class HomeControllerTest extends TestCase {
public function testIndex()
{
$post_collection = new StdClass();
Post::shouldRecieve('all')
->once()
->andReturn($post_collection);
$this->client->request('GET', 'posts');
$this->assertViewHas('posts');
}
}
I understand both methods but I don't understand why I should or when I should use one method over the other. For example, I've tried to use the DI route with the Auth class but it doesn't work so I have to use the Facade Mocks. Any calcification on this issue would be greatly appreciated.
Although you use dependency injection on Option #1, your controller is still coupled with the Eloquent ORM. (Note that i avoid to use the term Model here because in MVC the Model is not just a class or an object but a layer. It's your business logic.).
Dependency Injection allows for Dependency Inversion but they are not the same thing. According to the Dependency Inversion principle both high and low level code should depend on abstractions. In your case the high level code is your controller and the low level code is the Eloquent ORM that fetches data from MySQL, but as you can see none of them depends on abstractions.
As a consequence, you are not able to change your data access layer without affecting your controller. How would you go about changing for example from MySQL to MongoDB or to the File System? To do this you have to use repositories (or whatever you want to call it).
So create a repositories interface that all your concrete repository implementations (MySQL, MongoDB , File System etc.) should implement.
interface PostRepositoriesInterface {
public function getAll();
}
and then create your concrete implementation e.g. for MySQL
class DbPostRepository implements PostRepositoriesInterface {
public function getAll()
{
return Post::all()->toArray();
/* Why toArray()? This is the L (Liskov Substitution) in SOLID.
Any implementation of an abstraction (interface) should be substitutable
in any place that the abstraction is accepted. But if you just return
Post:all() how would you handle the situation where another concrete
implementation would return another data type? Probably you would use an if
statement in the controller to determine the data type but that's far from
ideal. In PHP you cannot force the return data type so this is something
that you have to keep in mind.*/
}
}
Now your controller must type hint the interface and not the concrete implementation. This is what "Code on an interface an not on implementation" is all about. This is Dependency Inversion.
class HomeController extends BaseController {
public function __construct(PostRepositoriesInterface $repo)
{
$this->repo= $repo;
}
public function index()
{
$posts = $this->repo->getAll();
return View::make( 'posts' , compact( $posts ) );
}
}
This way your controller is decoupled from your data layer. It's open for extension but closed for modification. You can switch to MongoDB or to the File System by creating a new concrete implementation of PostRepositoriesInterface (e.g. MongoPostRepository) and change only the binding from (Note that i don't use any namespaces here):
App:bind('PostRepositoriesInterface','DbPostRepository');
to
App:bind('PostRepositoriesInterface','MongoPostRepository');
In an ideal situation your controller should contain only application and not business logic. If you ever find yourself wanting to call a controller from another controller its a sign that you've done something wrong. In this case your controllers contain too much logic.
This also makes testing easier. Now you are able to test your controller without actually hitting the database. Note that a controller test must test only if the controller functions properly which means that the controller calls the right method, gets the results and pass it to the view. At this point you are not testing the validity of the results. This is not controller's responsibility.
public function testIndexActionBindsPostsFromRepository()
{
$repository = Mockery::mock('PostRepositoriesInterface');
$repository->shouldReceive('all')->once()->andReturn(array('foo'));
App::instance('PostRepositoriesInterface', $repository);
$response = $this->action('GET', 'HomeController#index');
$this->assertResponseOk();
$this->assertViewHas('posts', array('foo'));
}
EDIT
If you choose to go with option #1 you can test it like this
class HomeControllerTest extends TestCase {
public function __construct()
{
$this->mock = Mockery::mock('Eloquent', 'Post');
}
public function tearDown()
{
Mockery::close();
}
public function testIndex()
{
$this->mock
->shouldReceive('all')
->once()
->andReturn('foo');
$this->app->instance('Post', $this->mock);
$this->call('GET', 'posts');
$this->assertViewHas('posts');
}
}

Laravel's mass asignment in repository

I was using the mass assignment feature of the Laravel 4.1 framework, and the problem began to appear when I decided to make my controllers more flexible by adding Repositories. When using this pattern I don't quite understand how to implement standard Eloquent methods as fill() and save()
This is my repository code :
class EloquentUserRepository implements UserRepository
{
public function paginate($perPage = 15, $order = 'ASC', $orderBy = 'id')
{
return User::orderBy($orderBy, $order)->paginate($perPage);
}
public function getInstance()
{
return new User;
}
public function findAll()
{
return User::all();
}
public function find($id)
{
return User::find($id);
}
public function fill(array $data)
{
}
public function update(array $data)
{}
public function save()
{}
public function create(array $data)
{
return User::create($data);
}
public function delete($id)
{
$user = User::find($id);
if ($user)
{
return $user->delete();
}
return false;
}
}
Has anybody made something similar? Thank you.
Maybe this is not the role of a "repository" class. Look at the meaning of repository : "a place where things are stored and can be found". I think you have to find an other pattern.
You can use a kind of procedural methods :
UserSaver::fill($user, $arrayOfAttributes);
UserSaver::save($user);
Or build a class on top of your user class :
$abstractUser = new AbstractUser($user);
$abstractUser->fill($arrayOfAttributes);
$abstractUser->save();
And I'm sure other (maybe better) patterns exist.
I'm not a big fan of all of this patterns because I think that Eloquent is already a good abstraction layer (or maybe because my projects are too small). If I want to change the implementation of all() or save(), I override them in a specific model or in my class BaseModel which inherits all others. I also use events like saving and a lot of query scopes. Then my model and controller layers are very loosely coupled.

Categories