share method between controllers Laravel 5.2 - php

In several controllers i have to use the same method to show results as table with column sorting functionality:
public function showSearchResults(Request $req){
$query=Service::where('title', $req->search);
// Columns sorting
if ($req->has('order')){
$order= $req->order=='asc' ? 'asc' : 'desc';
$order_inverse=$req->order=='asc' ? 'desc' : 'asc';
} else {
$order='desc';
$order_inverse='asc';
}
...
$url=$req->url().'?'.http_build_query($req->except('sortby','order','page'));
$results=$query->with('type')->paginate(15)->appends($req->all());
return View::make('services.search_results')
->with('results', $results)
->with('url',$url)
->with('sortby', $sortby)
->with('order', $order)
->with('order_inverse', $order_inverse);
}
What is the best approach to avoid DRY in such case?

Sharing methods among Controllers with Traits
Step 1: Create a Trait
<?php // Code in app/Traits/MyTrait.php
namespace App\Traits;
trait MyTrait
{
protected function showSearchResults(Request $request)
{
// Stuff
}
}
Step 2: use the Trait in your Controller:
<?php // Code in app/Http/Controllers/MyController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Traits\MyTrait; // <-- you'll need this line...
class MyController extends Controller
{
use MyTrait; // <-- ...and also this line.
public function getIndex(Request $request)
{
// Now you can call your function with $this context
$this->showSearchResults($request);
}
}
Now you can use your Trait in any other controller in the same manner.
It is important to note that you don't need to include or require your Trait file anywhere, PSR-4 Autoloading takes care of file inclusion.
You can also use Custom Helper classes as others have mentioned but I would recommend against it if you only intend to share code among controllers. You can see how to create custom helper classes here.

You can use traits (as ventaquil suggested) or you can create custom helpers file and add helpers there.
After that, you'll be able to use this helper from any class (controllers, models, custom classes, commands etc) like any other Laravel helper.

Use traits? More available here: http://php.net/manual/en/language.oop5.traits.php

You can create a helper file in your app directory. for eg. MethodHelper.php
In this file you can mention the method that you require using anywhere.
For instance,
<?php namespace App;
class MethodHelper
{
public static function reusableMethod()
{
//logic
}
}
You can use this method anywhere, by using the namespace and calling the method.
In the above eg.
The namespace would be:
The method call function would look like:
MethodHelper::reusableMethod();
You can send parameters too based on your functional requirements.
In your eg. you could have
public function showSearchResults(Request $req){
//
}
instead of reusableMethod().
Your call would be:
MethodHelper::showSearchResults($req);

Related

How to use a trait as a route controller method in laravel?

Target [App\Http\Controllers\Traits\FileUploadTrait] is not instantiable.
I get this error when trying to send a file upload to this route:
<?php
namespace App\Http\Controllers\Traits;
use Illuminate\Http\Request;
use Intervention\Image\Facades\Image;
trait FileUploadTrait
{
/**
* File upload trait used in controllers to upload files
*/
public function saveFiles(Request $request)
{
//some file upload code
}
}
in my route:
Route::post('upload/files', ['uses' => 'Traits\FileUploadTrait#saveFiles', 'as' => 'media.upload']);
how to use a trait as a route controller#method?
You can't use a Trait as controller since Trait's are not classes, but actually they "are a mechanism for code reuse in single inheritance languages such as PHP." (See php docs).
A trait is not instantiable, this means that under the hood, Laravel can't do
$controller = new FileUploadTrait() to use it.
To use a trait, you must include it on some class, for example:
class MyController {
use FileUploadTrait;
}
Then you define your routes to use that class that you define.
traits arent callable... which means :) you cannot call them :Dlol, sorry - couldnt help myself... anyway try with something like this :)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Intervention\Image\Facades\Image;
class MahController extends Controller {
use App\Http\Controllers\Traits\FileUploadTrait;
}
Traits cannot be 'instantiated', you add them on objects.

Avoid Laravel facade on controller

I'm using Laravel 5.5 and trying to get used to code by psr-2 standard (just started learning). I analyze all my code with Quafoo QA and go step by step fixing the errors and record them.
By using facades i get this error "Avoid using static access to class". Because of it i'm trying to avoid using them.
On my controller i have this code:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Events\FileLoaded;
use Illuminate\Support\Facades\Input;
use Illuminate\Auth\Middleware\Authenticate;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use \Illuminate\Contracts\View\Factory as ViewFactory;
class LoadDataController extends Controller
{
public function index()
{
$viewfactory = app(ViewFactory::class);
return $viewfactory->make('LoadData/index');
}
//more code
}
Besides the View Facade i also use DB, Input, Validator and Storage
Is this the correct way, are there others?
You don't need to avoid Facades - they are a key part of the framework. But if you want to, you can use dependency injection to include the classes you need as arguments in the controller methods:
class LoadDataController extends Controller
{
public function index(ViewFactory $viewFactory)
{
return $viewfactory->make('LoadData/index');
}
//more code
}
Or if you need that in all the controller methods:
class LoadDataController extends Controller
{
private $viewFactory;
public function __construct(ViewFactory $viewFactory)
{
$this->viewFactory = $viewFactory;
}
public function index()
{
return $this->viewFactory->make('LoadData/index');
}
//more code
}
Of course, this doesn't actually change the functionality of the code you've written, it just rearranges it. I wouldn't take the word of the code analyzer you mentioned as something you are doing wrong. These are standard patterns to use in Laravel.

Laravel global variable for base layout

I have a few things that I want to present on every page, like $gameAccounts = Auth::user()->gameAccounts()->get();
What would be the best approach to present this variable globally, on each page?
If you need it only for the views you can use a view composer in your AppServiceProvider
public function boot()
{
View::composer('layouts.base', function ($view) {
$view->with('gameAccounts', Auth::user()->gameAccounts);
});
}
If you need it globally you can store it in a config also in AppServiceProvider.
For shared functionality I would recommend to write a trait. The trait can be used throughout your controllers and provide the same functionality for all.
Example:
trait GameSettingsTrait
{
public function getUserGameAccounts()
{
return Auth::user()->gameAccounts()->get();
}
}
In your controller do:
class IndexController extends Controller
{
use GameSettingsTrait;
...
Another approach would be to put the logic in the base controller in app/Http/Controllers/Controller.php. Btw, there you can see that it already uses traits for other functionality.
You need to use view composer.
Look at the doc.
https://laravel.com/docs/5.2/views#view-composers
Use view composer is a best way to do this.
Just add this rows to your AppServiceProvider.php inside boot method:
public function boot()
{
//
view()->composer('*', function ($view) {
$view->with('user', Auth::user()->gameAccounts()->get());
});
}

Use statement for child classes

I have a Controller class in which I have to use several namespaces like :
<?php
use Respect/Validation/Validator;
use Blah/blah/Foo;
class Controller {}
Now what I want that in every controller files that extends my Controller class, I do not have to write the use statements again and again.
This is something I want similar to what Laravel has done in his alias section.
How would I achieve this thing ? So for example when I do :
<?php
class HomeController extends Controller {
public function index()
{
$data = '';
Validator::arr($data); // Validator not found
}
}
This is unfortunately what you would have to do if using static methods. You could also have a Controller method (the constructor even) to inject the validator into the class, which the child classes can then use. You would the use public instance variables on the validator.
The benefit is that you do not need to specify the NS for your dependencies again and, especially so, your code is cleaner since you do not have a hard dependency on the Validator class.

Laravel 4: Using views in a package

I've created a very basic app in Laravel 4, it's something I'll be reusing a lot in various projects so it made sense to convert it to a package before i got too far, but I'm struggling to make the changes to get it working, which I think is largely due to figuring out how to access the various objects that are normally available in an app, eg View::make
I had the following code working in an app:
class PageController extends BaseController {
public function showPage($id)
{
//do stuff
return View::make('page/showPage')
->with('id', $id)
->with('page', $page);
}
for the package I have the following:
use Illuminate\Routing\Controllers\Controller;
use Illuminate\Support\Facades\View;
class PageController extends Controller {
public function showPage($id)
{
//do stuff
return View::make('page/showPage')
->with('id', $id)
->with('page', $page);
}
However this does not load the blade template which is located at:
workbench/packagenamespace/package/src/views/page/showPage.blade.php
nor does this work:
return View::make('packagenamespace/package/src/page/showPage')
Also, I am wondering if what i have done with the use statements where I use the facade object correct, to me it seems like there should be a neater way to access things like the View object?
You should read the docs: http://four.laravel.com/docs/packages
Specifically the part explaining loading views from packages ;)
return View::make('package::view.name');
If you don' want to use:
use Illuminate\Support\Facades\View;
Just do:
use View;
Or even without the use statement:
\View::make('package::view.name');
// Serviceprovider.php
$this->loadViewsFrom(__DIR__.'/resources/views', 'laratour');
// Controller
<?php
namespace Mprince\Laratour\Controllers;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class LaratourController extends Controller
{
public function index()
{
return view('laratour::index');
// return view('packageName::bladeFile');
}
//END
}

Categories