This question already has answers here:
Laravel 5 - global Blade view variable available in all templates
(9 answers)
Closed 1 year ago.
I have products in a database that are essentially used on every single page. Instead of having to query the database, something like this:
$products = DB::table("products")->get();
And then passing it into view:
return view("site.products", array(
'products' => $products,
));
I don't want to do this for every view. Instead, I want $products to be available to ALL templates by default... so that I can do this:
#foreach ($products as $product)
... etc
How would I declare it in a global way to achieve this?
You can add below code in the boot method of AppServiceProvider
public function boot()
{
if (!$this->app->runningInConsole()) {
$products = DB::table("products")->get();
\View::share('products', $products);
}
}
Read more from here
A recommended way of doing this is to add a middleware that you apply to all the routes that you want to affect.
Middleware
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\View;
class GlobalVariablesMiddleware
{
$myVariable = "Value For Everyone";
View::share(['globalValue' => $myVariable]);
}
Add it to your kernel.php in the Http folder
protected $routeMiddleware = [
...
'myMiddleware' => \App\Http\Middleware\ GlobalVariablesMiddleware::class,
];
Once this is setup, you can easily apply it to individual routes or grouped ones to achieve what you are looking for
// Routes that will have the middleware
Route::middleware(['myMiddleware'])->group(function () {
// My first route that will have the global value
Route::resource('/profile', App\Http\Controllers\ProfileController::class);
// My second route that will have the global value
Route::resource('/posts', App\Http\Controllers\PostController::class);
});
By doing it this way, you can easily control the data in the future if you would chose not to have the data global.
Related
I have a blade component containing $attributes; It would be a bag of attributes when the component called from another blade template but when from the controller via view() the $attributes is undefined! How can I pass data as $attributes from the controller?
Component: sample
<div {{ $attributes->except('content') }}>{{ $content }}</div>
Template: works well.
...
<x-sample class="test" content="test"/>
...
Controller: Error Undefined variable $attributes
$attributes = ['class' => 'test', 'content' => 'test'];
view('components.sample', $attributes)->render();
view('components.sample', ['attributes' => $attributes])->render();
view('components.sample')->with($attributes)->render();
UPDATE (Solution):
It works:
view('components.sample', [
'prop1' => '...',
'prop2' => '...',
'attributes' => new ComponentAttributeBag(['attr1' => '...', 'attr2' => '...']),
])->render();
what is happening is components inherit variable from view, if you define your variable from home view for example, it should work in component from that view. Then when you include the view to about page the variables wont be recognized since they are not inherited from about view. And in Laravel you cannot pass data directly form controller to component. but laravel have solved it by View Composer.
Since I dont like too much complication especially with providers I edited my AppServiceProvider you can create your own provider.
in YourServiceProvider in my case AppServiceProvider in boot() function there are three options you can use one. I recommend there 3rd one since its clean
public function boot()
{
// option1 - Every single view
View::share('shops', Shop::orderBy('name')->get());
// option2 - Gradular views with wildcards
View::composer(['client.*'], function ($view){
$view->with('shops', Shop::orderBy('name')->get());
});
// option3 - Dedicated class
View::composer(['client.includes.*','client.create-product','client.cart'], ShopsComposer::class);
View::composer(['client.includes.*','client.cart'], CartComposer::class);
}
If you use the 3rd method you have to create ViewComposer class
<?php
namespace App\Http\View\Composers;
use App\Models\CartItem;
use Illuminate\Support\Facades\Session;
use Illuminate\View\View;
class ShopsComposer
{
public function compose(View $view){
$shop = auth()->user()->shops->sortBy('name');
$cartItem = new CartItem();
$cartCount = 0;
if (Session::has('cartId')) {
$carts = Session::get('cartId');
$cartItems = $cartItem->with('products')->where('cart_id', '=', $carts->id)->get();
foreach ($cartItems as $item) {
$cartCount += $item->quantity;
}
}
$view->with('shopsComposer', $shop)->with('cartCount', $cartCount);
}
}
The variables you define there will be available to all the views. that includes components. Since component inherit variable from view file.
I am sorry I had to share my working example, since I am running out of time. I hope it works if I understood well your question.
We can use a custom $customPageName in laravel's paginate() function's 3rd parameter.By that we can use site.com/url?$customPageName=n . Is there any way we can set the custom page name globally ? In AppServiceProvider or somewhere like that?So we don't need to define every time?
There are many possible solution. I give you two that IMO are the best:
1: Create a trait and use it in every component
I love to use traits, they avoid a lot of redundancy and can be easily invoked everywhere:
trait PaginationTrait
{
const PAGE_NAME = 'your_global_value'
public function paginate($query, $perPage, $columns = [''], $page_name = null)
{
return $query->paginate($perPage, $columns, $page_name ?: self::PAGE_NAME);
}
}
This solution requires anyway that you pass as input a Builder instance, and may cause a logic problem. So let's move to the next solution
2: Config files
Simply set a variable in an existing config file or in a new config file:
// Example: app.php
return [
// [...]
'page_name' => 'your_custom_value' // or env('APP_PAGE_NAME', 'your_custom_value'),
// [...]
];
And in your controller you can retrive the value as follows:
public function index() {
// [...]
$result = MyModel::paginate($per_page, $columns, config('app.page_name'));
}
3: App\Http\Controllers\Controller
The third solution is the easiest. Set a constant in your App\Http\Controllers\Controller class (as I wrote for the trait part) and by the OOP rules, it will be inherited to all your controllers:
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
const PAGE_NAME = 'your_global_value';
}
And in your controller:
public function index() {
// [...]
$result = MyModel::paginate($per_page, $columns, self::PAGE_NAME);
}
In my opinion, if you simply have to set this global variable, solution 2 and 3 are the best... If you have to create a custom pagination logic, then I think that creating a specific Trait or Class is a good choice
Let's say I have a function in my controller which retrieves users looking something like this:
public function index($category) {
// retrieve users depending on category or all
}
Now is there a way to make named routes to include the function parameter like so:
Route::get('passengers', 'Controller#index(1)')->name('passengers');
Route::get('attendees', 'Controller#index(2)')->name('attendees');
This way they can all use the same function
No you can not pass a parameter the action name, and there is a problem in you routing logic :
Route::get('/{categoryName}', 'Controller#index')->name('index');
And in the controller you will for example get the category by name like this :
public function index($categoryName) {
$category = Category::where('name', $categoryName)->first();
// use $ category as you please ;)
}
In the blade :
route('index', ['categoryName' => $category->name])
If the named route defines parameters, you may pass the parameters as the second argument to the route function. The given parameters will automatically be inserted into the URL in their correct positions
https://laravel.com/docs/5.5/routing#named-routes
So, use route() helper like this:
route('passengers', ['category' => 1])
Then you need to add {category} to the route. Also, it's really better to use show() instead of index() here. So, your route will look like this:
Route::get('passengers/{category}', ['as' => 'passengers', 'uses' => 'Controller#show']);
Yes, you can define the param in the url like so:
Route::get('passengers/{yourParam}', 'Controller#index')->name('passengers');
View in docs
Route::get( '{category}', [ 'as' => 'users', 'uses' => 'Controller#index' ]);
Remember to add this route at the end of your routes file in order to not to collide with any other route.
Now in your controller
use Illuminate\Http\Request;
public function index(Request $request)
{
$category = $request->query('category');
// $category will be passengers, attendees, etc
}
Your routes will be
/passengers can be accessed as route('users', ['category' => 'passengers'])
/attendees can be accessed as `route('users', ['category' => 'attendees'])
I need to get a list of the paths of all routes programmatically.
I tried Route::getRoutes() - not working in L5. RouteCollection::getRoutes() - is not a static method.
I bet I can get the RouteCollection from $request, but I don't know how.
Route::getRoutes(); should work, you might have forget to import the route class (facade). Then you iterate the list:
$routeList = Route::getRoutes();
foreach ($routeList as $value)
{
echo $value->getPath();
}
Remeber to import
use Illuminate\Support\Facades\Route;
This is tested on Laravel 5.2
Documenation
First
use Illuminate\Support\Facades\Route;
For all routes use this code
$routeList=Route::getRoutes();
foreach ($routeList as $value) {
echo $value->getPath();
}
For current route name use this code
$currentPath= Route::getFacadeRoot()->current()->uri();
For details information, read this two posts,
All Routes
and Current Route
I like to use resource controllers in Laravel, as it makes me think when it comes to data modelling. Up to now I’ve got by, but I’m now working on a website that has a public front-end and a protected back-end (administration area).
I’ve created a route group which adds an “admin” prefix, like so:
Route::group(array('before' => 'auth', 'prefix' => 'admin'), function()
{
Route::resource('article', 'ArticleController');
Route::resource('event', 'EventController');
Route::resource('user', 'UserController');
});
And I can access the methods using the default URL structure, i.e. http://example.com/admin/article/1/edit.
However, I wish to use a different URL structure on the front-end, that doesn’t fit into what resource controllers expect.
For example, to access an article, I’d like to use a URL like: http://example.com/news/2014/06/17/some-article-slug. If this article has an ID of 1, it should (under the hood) go to /article/1/show.
How can I achieve this in Laravel? In there some sort of pre-processing I can do on routes to match dates and slugs to an article ID, and then pass that as a parameter to my resource controller’s show() method?
Re-visiting this, I solved it by using route–model binding and a pattern:
$year = '[12][0-9]{3}';
$month = '0[1-9]|1[012]';
$day = '0[1-9]|[12][0-9]|3[01]';
$slug = '[a-z0-9\-]+';
// Pattern to match date and slug, including spaces
$date_slug = sprintf('(%04d)\/(%02d)\/(%02d)\/(%s)', $year, $month, $day, $slug);
Route::pattern('article_slug', $date_slug);
// Perform the route–model binding
Route::bind('article_slug', function ($slug) {
return Article::findByDateAndSlug($date_slug);
});
// The actual route
Route::get('news/{article_slug}', 'ArticleController#show');
This then injects an Article model instance into my controller action as desired.
One simple solution would be to create one more route for your requirement and do the processing there to link it to the main route. So, for example:
//routes.php
Route::get('/arical/{date}/indentifier/{slug}', array (
'uses' => 'ArticleController#findArticle'
));
//ArticleContoller
public function findArticle($date,$slug){
$article = Article::where('slug','=','something')->first(); //maybe some more processing;
$article_id = $article->id;
/*
Redirect to a new route or load the view accordingly
*/
}
Hope this is useful.
It seems like if Laravel 4 supports (:all) in routing, you would be able to do it with ease, but unfortunately (:all) is not supported in Laravel 4.
However, Laravel 4 allows detecting routes by regular expression, so we can use ->where('slug', '.*').
routes.php: (bottom of the file)
Route::get('{slug}', 'ArticleController#showBySlug')->where('slug', '.*');
Since Laravel will try to match the top most route in routes.php first, we can safely put our wildcard route at the bottom of routes.php so that it is checked only after all other criteria are already evaluated.
ArticleController.php:
class ArticleController extends BaseController
{
public function showBySlug($slug)
{
// Slug lookup. I'm assuming the slug is an attribute in the model.
$article_id = Article::where('slug', '=', $slug)->pluck('id');
// This is the last route, throw standard 404 if slug is not found.
if (!$article_id) {
App::abort(404);
}
// Call the controller's show() method with the found id.
return $this->show($article_id);
}
public function show($id)
{
// Your resource controller's show() code goes here.
}
}
The code above assumes that you store the whole URI as the slug. Of course, you can always tailor showBySlug() to support a more advanced slug checking.
Extra:
You could also do:
Route::get('{category}/{year}/{slug}', 'ArticleController#showBySlug')->where('slug', '.*');
And your showBySlug() would just have additional parameters:
public function showBySlug($category, $year, $slug)
{
// code
}
Obviously you can extend to month and day, or other adaptations.