Includin many models in the routes `web.php` file practice - php

I have many routes, and for a single page (my homepage) I need to sample almost all of my models. I do it like that:
use App\Models\Aa;
use App\Models\Bb;
use App\Models\Cc;
// and so on ...
Route::get('/', function () {
$data_for_view = [
'aa' => Aa::where('show', true)->inRandomOrder()->limit(4)->get(),
'bb' => Bb::where('published', true)->limit(4)->get(),
'cc' => Cc::where('published', true)->limit(4)->get()
// ... and so on
// ...
];
return view('welcome', $data_for_view);
});
However, this is the only route that uses so many models. so my questions is: is there a better way to achieve that goal?
is it a standard practice?

In the beginning you should use some controller for that and wrap your all logic inside method
For example:
in web.php route file add this:
use App\Http\Controllers\SomeController;
// SomeController
Route::get('/', [SomeController::class, 'index']);
In SomeController
<?php
namespace App\Http\Controllers;
use App\Models\Aa;
use App\Models\Bb;
use App\Models\Cc;
class SomeController extends Controller
{
public function index()
{
$data_for_view = [
'aa' => Aa::where('show', true)->inRandomOrder()->limit(4)->get(),
'bb' => Bb::where('published', true)->limit(4)->get(),
'cc' => Cc::where('published', true)->limit(4)->get()
// ... and so on
// ...
];
return view('welcome',compact('data_for_view'));
}
}
You can use artisan command for creating controller.
php artisan make:controller SomeController

Related

Error with Invoke type controller in Laravel

I'm having problems with and invoke type controller.
After I create the controller with php artisan make:controller -i and add the route, when go to the route it tells me that the Invoke function doesn't exist.
Here is the route I'm using:
Route::get('/portfolio','PortfolioController');
And here is the code of the controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PortfolioController extends Controller
{
public function __invoke(Request $request)
{
/** #var array $portafolio */
$portafolio = [
["title" => "Proyecto #1"],
["title" => "Proyecto #2"],
["title" => "Proyecto #3"],
["title" => "Proyecto #4"],
];
return view("portfolio", compact("portafolio"));
}
}
I don't really get why this error occurs, because the invoke function is clearly there, so if anyone knows what could be the problem I will be really grateful.
I'm using the last version of Laravel.
You need to use the fully qualified class name as in the documentation:
use App\Http\Controllers\PortfolioController;
Route::get('/portfolio', PortfolioController::class);

Laravel Parameter in every url

I have read almost everything in web and documentation but i can't find solution for my Problem.
I have a variable stored in Session , then I want to put this variable in every url generated by route('some-route') .
In Session I have sub = "mysubid"
When I generate Route route('my-route') I want to pass this sub parameter in query string: http://domain.dom/my-route-parameter?sub=mysubid
Can you help me to solve This problem? Any helpful answer will be appreciated;
You can use the Default Values feature.
First create a new middleware php artisan make:middleware SetSubIdFromSession. Then do the following:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\URL;
class SetSubIdFromSession
{
public function handle($request, Closure $next)
{
URL::defaults(['sub' => \Session::get('sub')]);
return $next($request);
}
}
At the end register your new middleware in app/Http/Kernel.php by adding it to $routeMiddleware.
protected $routeMiddleware = [
// other Middlewares
'sessionDefaultValue' => App\Http\Middleware\SetSubIdFromSession::class,
];
Add {sub} and the middleware to your route definition:
Route::get('/{sub}/path', function () {
//
})
->name('my-route')
->middleware('sessionDefaultValue');
Since you want this on every web route you can also add the middleware to the web middleware group:
protected $middlewareGroups = [
'web' => [
// other Middlewares
'sessionDefaultValue',
],
'api' => [
//
]
];
Try this , You need to create middleware php artisan make:middleware SetSubSession
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\URL;
class SetSubsSession
{
public function handle($request, Closure $next)
{
if(session('sub')){
$url = url()->full();
return redirect($url.'?sub='.session('sub'));
}
return $next($request);
}
}
in app/http/Kernel.php
protected $routeMiddleware = [
........
'setsubsession' => \App\Http\Middleware\SetSubsSession::class,
]
in route.php add
Route::group(['middleware' => 'setsubsession'], function(){
//and define all the route you want to add sub parameter
});
using this you don't need to change all your routes.This will automatic add "sub" in the route define in that middleware.

How to append data to default laravel view after application creation [duplicate]

How can I in Laravel 5 make global variable which will be available in all Blade templates?
Option 1:
You can use view::share() like so:
<?php namespace App\Http\Controllers;
use View;
//You can create a BaseController:
class BaseController extends Controller {
public $variable1 = "I am Data";
public function __construct() {
$variable2 = "I am Data 2";
View::share ( 'variable1', $this->variable1 );
View::share ( 'variable2', $variable2 );
View::share ( 'variable3', 'I am Data 3' );
View::share ( 'variable4', ['name'=>'Franky','address'=>'Mars'] );
}
}
class HomeController extends BaseController {
//if you have a constructor in other controllers you need call constructor of parent controller (i.e. BaseController) like so:
public function __construct(){
parent::__construct();
}
public function Index(){
//All variable will be available in views
return view('home');
}
}
Option 2:
Use a composer:
Create a composer file at app\Composers\HomeComposer.php
NB: create app\Composers if it does not exists
<?php namespace App\Composers;
class HomeComposer
{
public function compose($view)
{
//Add your variables
$view->with('variable1', 'I am Data')
->with('variable2', 'I am Data 2');
}
}
Then you can attached the composer to any view by doing this
<?php namespace App\Http\Controllers;
use View;
class HomeController extends Controller{
public function __construct(){
View::composers([
'App\Composers\HomeComposer' => ['home'] //attaches HomeComposer to home.blade.php
]);
}
public function Index(){
return view('home');
}
}
Option 3:
Add Composer to a Service Provider, In Laravel 5 I prefer having my composer in App\Providers\ViewServiceProvider
Create a composer file at app\Composers\HomeComposer.php
Add HomeComposer to App\Providers\ViewServiceProvider
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use View;
use App\Composers\HomeComposer;
use Illuminate\Support\Facades\Blade;
class ViewServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//add to all views
view()->composer('*', HomeComposer::class);
//add to only home view
//view()->composer('home', HomeComposer::class);
}
}
Create a new Service Provider as suggested in here
Add your new Service Provider to the configuration file (config/app.php).
In the boot method of your new Service Provider use:
View::share( 'something_cool', 'this is a cool shared variable' );
Now you are ready to use $something_cool in all of your views.
Hope this helps.
Searching for solution of the same problem and found the best solution in Laravel documentation. Just use View::share in AppServiceProvider like this:
View::share('key', 'value');
Details here.
You can do this with view composers. View composers are executed when a template is loaded. You can pass in a Closure with additional functionality for that view. With view composers you can use wildcards. To make a view composer for every view just use a *.
View::composer('*', function($view)
{
$view->with('variable','Test value');
});
You can also do this without a closure as you can see in the docs.
View::composer('*', 'App\Http\ViewComposers\ProfileComposer');
The profile composer class must have a compose method.
View composers are executed when a view is rendered. Laravel has also view creators. These are executed when a view is instantiated.
You can also choose to use a BaseController with a setupLayout method. Then every view which you will load is loaded through the setupLayout method which adds some additional data. However, by using view composers you're pretty sure that the code is executed. But with the BaseController approach you've more flexibility because you can skip the loading of the extra data.
EDIT: As mentioned by Nic Gutierrez you can also use view share.
Also, you can do this in the Route.php file:
view()->share('variableName', $variable);
I would rather use middleware with the view() facade helper. (Laravel 5.x)
Middleware is easier to mantain and does not make a mess in the controllers class tree.
Steps
Create the Middleware
/app/Http/Middleware/TimezoneReset.php
To create a middleware you can run php artisan make:middleware GlobalTimeConfig
share() the data you need shared
<?php
namespace App\Http\Middleware;
use Closure;
class GlobalTimeConfig
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$time_settings = [
'company_timezone' => 'UTC',
'company_date_format' => 'Y-m-d H:i:s',
'display_time' => true,
];
view()->share('time_settings', $time_settings);
return $next($request);
}
}
Register the newly created middleware
Add the middleware to your middleware route group as per example below
/app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\GlobalTimeConfig::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
Access data from templates
Access the data from any template with the given key in the View::share() method call
eg.:
Company timezone: {{ $time_settings['company_timezone'] }}
EDIT:
Nic Gutierrez's Service Provider answer might be a better (or the best) solution.
and you can give array not just View::share('key', 'value');
can put array like View::share(['key'=>'value','key'=>'value'])
You can add in Controller.php file:
use App\Category;
And then:
class Controller extends BaseController {
public function __construct() {
$categories = Category::All();
\View::share('categories', $categories);
}
}
you can flash it into the session, you can define it in the .env file (static vars)

Can I remove Request from Laravel Controller

Can I remove the following line of code
use Illuminate\Http\Request;
form laravel Controller? Is this a good practice?
For example, my HomeController:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
$totals = [
'customers' => \App\Customer::count(),
'jobs' => \App\Job::count(),
'invoices' => \App\Invoice::count(),
];
$data = [
'page_title' => 'Dashboard',
'totals' => $totals
];
return view('home', $data);
}
}
Here I don't need the "Request", because none of the functions doesn't use that parameter.
To obtain an instance of the current HTTP request via dependency injection, you should type-hint the Illuminate\Http\Request class on your controller method. The incoming request instance will automatically be injected by the service container.
So if you don't want To obtain an instance of the current HTTP request then remove it.
Yes if you are using just select data query you can go ahead and remove this line. It's need where you will use any get or post form in your class function.

Laravel Route Model Binding to Route::controller()

I've got the following routes:
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
'admin' => 'AdminController',
'site' => 'SiteController'
]);
And then I've got the following method in the SiteController:
/**
* Get site details and pass to view
*
* #param Site $site
* #return mixed
* #internal param $site_id
*/
public function getDetails( Site $site )
{
return $site;
}
When I go to the URL site.com/site/details/13 it doesn't return the site object.
I've added $router->model( 'one', 'App\Site' ); into the RouteServiceProvider and it works, but what if later down the road I want to add another controller like this but use it for jobs, and use the getDetails method again and pass through the App\Job object? It will automatically send the App\Site model instead.
So is there a way I can prevent this from happening?
My limited knowledge of Laravel tells me you can't have a model/object as parameter in your route controller functions, and that you don't need something like $router->model( 'one', 'App\Site' ); to do this.
I'm assuming you'd want to do something like this:
As for your routes:
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
'admin' => 'AdminController',
'site' => 'SiteController',
'jobs' => 'JobController',
]);
In your SiteController:
use Illuminate\Http\Request;
use App/Site; //replace with namespace of model
public function getDetails($id)
{
//code for fetching the site object, depends on how your structure is,
//like $site = App\Site::find($id); etc
return $site;
}
Similarly, your JobController will be something like:
use Illuminate\Http\Request;
use App/Job; //replace with namespace of model
public function getDetails($id)
{
//code for fetching the job object, depends on how your structure is,
//like $job = App\Job::find($id); etc
return $job;
}
Look here: Laravel Docs - Implicit Controllers

Categories