Symfony2 - get main request's current route in twig partial/subrequest - php

In Twig partial rendered by separate controller, I want to check if current main route equals to compared route, so I can mark list item as active.
How can I do that? Trying to get current route in BarController like:
$route = $request->get('_route');
returns null.
Uri is also not what I'm looking for, as calling below code in bar's twig:
app.request.uri
returns route similar to: localhost/_fragment?path=path_to_bar_route
Full example
Main Controller:
FooController extends Controller{
public function fooAction(){}
}
fooAction twig:
...some stuff...
{{ render(controller('FooBundle:Bar:bar')) }}
...some stuff...
Bar controller:
BarController extends Controller{
public function barAction(){}
}
barAction twig:
<ul>
<li class="{{ (item1route == currentroute) ? 'active' : ''}}">
Item 1
</li>
<li class="{{ (item2route == currentroute) ? 'active' : ''}}">
Item 2
</li>
<li class="{{ (item3route == currentroute) ? 'active' : ''}}">
Item 3
</li>
</ul>

pabgaran's solution should work. However, the original problem occurs probably because of the request_stack.
http://symfony.com/blog/new-in-symfony-2-4-the-request-stack
Since you are in a subrequest, you should be able to get top-level (master) Request and get _route. Something like this:
public function barAction(Request $request) {
$stack = $this->get('request_stack');
$masterRequest = $stack->getMasterRequest();
$currentRoute = $masterRequest->get('_route');
...
return $this->render('Template', array('current_route' => $currentRoute );
}
Haven't run this but it should work...

I think that the best solution in your case is past the current main route in the render:
{{ render(controller('FooBundle:Bar:bar', {'current_route' : app.request.uri})) }}
Next, return it in the response:
public function barAction(Request $request) {
...
return $this->render('Template', array('current_route' => $request->query->get('current_route'));
}
And in your template compares with the received value.
Otherwise, maybe is better to use a include instead a render, if you don't need extra logic for the partial.

in twig you can send request object from main controller to sub-controller as parameter:
{{ render(controller('FooBundle:Bar:bar', {'request' : app.request})) }}
in sub-controller:
BarController extends Controller{
public function barAction(Request $request){
// here you can use request object as regular
$country = $request->attributes->get('route_country');
}
}

Related

Laravel: extending view from module

I have a module in my Laravel app (it could also be a composer package).
This module has a view composer which sets an array to all views containing the routes which should be included in the main navigation of the app.
The composer looks like this:
class ContactNavigationComposer
{
/**
* Bind data to the view.
*
* #param \Illuminate\View\View $view
* #return void
*/
public function compose(View $view)
{
$view->with('contactNavigation', config('contact.navigation'));
}
}
Then in the mainNav of the app this variable $contactNavigation becomes iterated to generate the entries:
<ul>
# ...
<li>
<a
class="{{ (request()->is('navigations/areas')) ? 'active' : '' }}"
href="{{ route('areas.index') }}">
Navigation Areas
</a>
</li>
<li>
<a
class="{{ (request()->is('languages')) ? 'active' : '' }}"
href="{{ route('languages.index') }}">
Languages
</a>
</li>
#foreach($contactNavigation as $text => $url)
<li>
<a
class="{{ (request()->is($url)) ? 'active' : '' }}"
href="{{ $url }}">
{{ $text }}
</a>
</li>
#endforeach
</ul>
This works perfectly fine but I was wondering if I can have this behavior in a more dynamic way and let composers of different modules using the same array e.g. $modules which contains navigation entries (and other stuff) of different modules.
This way I wouldn't have to add module extensions to the applications views later on.
So my suggested solution would be smth. like that:
class ContactNavigationComposer
{
/**
* Bind data to the view.
*
* #param \Illuminate\View\View $view
* #return void
*/
public function compose(View $view)
{
$view->with('modules.navigation.contact', config('contact.navigation'));
}
}
<ul>
# ...
#if (isset($modules['navigation']))
#foreach($modules['navigation'] as $moduleNavigation)
#foreach($moduleNavigation as $text => $url)
<li>
<a
class="{{ (request()->is($url)) ? 'active' : '' }}"
href="{{ $url }}">
{{ $text }}
</a>
</li>
#endforeach
#endforeach
#endif
</ul>
Of course with the dot-notation, this modules.navigation.contact-key is never translated into a view variable especially not as an array.
Is there any way to achieve something like that?
For those who came across this issue as well:
I changed my compose method to this
// ViewComposer
class ContactNavigationComposer extends BaseComposer
{
public function compose(View $view)
{
$moduleDataBag = $this->getModuleDataBag($view);
$moduleData = $moduleDataBag->mergeRecursive([
'contact' => [
'navigation' => config('contact.navigation.entries')
]
]);
$view->with('modules', $moduleData);
}
}
The getModuleDataBag-method comes from the BaseComposer-class from which each ViewComposer inherits from.
class BaseComposer
{
protected function getModuleDataBag(View $view)
{
if (!array_key_exists(config('contact.view.dataKey'), $view->gatherData())) {
ViewFacade::share(config('contact.view.dataKey'), collect([]));
}
return $view->gatherData()[config('contact.view.dataKey')];
}
}
The key in which all the configuration is stored is defined in the (publishable) config. This way this setup is flexible for modifications by the surrounding application.
(The package is called contact, that's why the view data key is in the contact-config.)
If the expected key is not found, the BaseComposer sets it to all views using the ::share-method on the View-facade as an empty collection.
Each ViewComposer always call the getModuleDataBag-method first to receive the current data probably already set by other ViewComposers.
As this $moduleDataBag is a collection, all additional values can be merged into as long as they are arrays. This is done recursively so that each existing key and value is preserved.
Hope that can help someone later on.

Want to add {{$value['category']}} in {{$value['category']}} in Laravel

I want to set {{$value['category']}} in {{route('')}}
This is my code:
#foreach($details as $value)
{{$value['category']}}
#endforeach
Controller function:
public function viewhome(Request $req){
$product = product::all()->unique('category');
return view('homepage',['details'=> $product]);}
Route:
Route::get('/homepage/db_val', 'HomeController#db_val')->name('db_val');
How to declare href properly. And what will be the route. Thank you.
1) warn : you call db_val function in HomeController but you show viewhome method in your question.
Route::get('/homepage/db_val', 'HomeController#<b>db_val</b>')->name('db_val');
if you want use method viewhome :
Route::get('/homepage/db_val', 'HomeController#<b>viewhome</b>')->name('db_val');
2) route is used with named route
you have a route named 'db_val' --> Route::.... ->name('db_val');
so it must be used like that ,
<a href='{{route('db_val')}}
3) in your case , assuming $value in foreach is an array with a 'category' index inside
you can use url instead route
#foreach($details as $value)
<a href="{{url('/your_url')}}/{{$value['category']}}">
link to {{$value['category']}}
</a>
#endforeach
4) but blade spirit is
route.php
Route::get('/showcategory/{id}','HomeController#showcategorie')->name('showcat');
view
#foreach($details as $value)
<a href="{{route('showcat', $value['category'])}}">
#endforeach
it means : you have one named route /showcategory with a parameter /{id}
Route:
Route::get('/homepage/db_val_1', 'HomeController#db_val_1')->name('db_val_1');
Route::get('/homepage/db_val_2', 'HomeController#db_val_2')->name('db_val_2');
Route::get('/homepage/db_val_3', 'HomeController#db_val_3')->name('db_val_3');
...
Controller Function :
public function db_val_1(Request $req){
$all = product::all()->where('category', 'db_val_1');
return view('homepage', ['details'=> $all]);
}
public function db_val_2(Request $req){
$all = product::all()->where('category', 'db_val_2');
return view('homepage', ['details'=> $all]);
}
public function db_val_3(Request $req){
$all = product::all()->where('category', 'db_val_3');
return view('homepage', ['details'=> $all]);
}
...
View home: In route the value will be ($value->category) like this.
#foreach($details as $value)
{{$value['category']}}
#endforeach
I got the solution. Thank you for helping.

Value into href to controller

my blade looks like this...
#if(count($alleSpiele) > 0)
<ul class="list-group">
#foreach($alleSpiele as $alleSpieleOutput)
{{$alleSpieleOutput->heimmannschaft}}
#endforeach
</ul>
#endif
This is my route...
Route::get('/spielerAuswahl', 'SpielplanController#getHeimGast');
Here is my controller...
public function getHeimGast(){
return view('test');
}
Now, my problem is to take the choosen variable from a href heimmannschaft from blade to route into the controller? What is the correct way to do this?
change your links to this
{{$alleSpieleOutput->heimmannschaft}}
then change your route to
Route::get('/spielerAuswahl/{value}', 'SpielplanController#getHeimGast');
then in the controller you can have the variable like this:
public function getHeimGast($value){
// use $value in your code
return view('test');
}

How to remove query string page from the first page of laravel pagination?

I'm using laravel-5.4 pagination like the following:
public function index()
{
$jobs = Job::paginate(5);
return view('job.index', compact('jobs'));
}
In the view:
{{ $jobs->links() }}
There is a problem of generating two typical pages: /job and /job?page=1 the two page has the same contents.
I want to do anything that removes the query string page from the first page of the pagination.
I have tried the following:
if ($jobs->onFirstPage()){
$jobs->setPageName('');
}
But this corrupt the pagination, i.e the links of pages does not load correctly and the query string value remains for all pages.
The effective solution for this issue that I have found is to edit the pagination template.
First publish the pagination template from the vendors using the following command from the root of the project:
php artisan vendor:publish --tag=laravel-pagination
Now a file at resources/views/vendor/pagination/default.blade.php should be found and it could be edited like the following using str_replace() for the urls of each page and back navigation button:
<li>{{$foxPrev}}</li>
and
<li>{{ $page }}</li>
Update:
A bug was found with ?page=10 so instead of using str_replace we should using preg_replace like the following:
<li>{{ $page }}</li>
Update 2:
In case of using any customized name for the page number parameter other than page, we could use the paginator getter for the $pageName property like the following in the pagination template:
<li>{{ $page }}</li>
To know more about how to use more than one pagination on the same page or how to customize the page number parameter $pageName from view, checkout this answer
You can extend LengthAwarePaginator method url($page)
/**
* Get the URL for a given page number.
*
* #param int $page
* #return string
*/
public function url($page)
{
if ($page <= 0) {
$page = 1;
}
// If we have any extra query string key / value pairs that need to be added
// onto the URL, we will put them in query string form and then attach it
// to the URL. This allows for extra information like sortings storage.
$parameters = ($page > 1) ? [$this->pageName => $page] : [];
if (count($this->query) > 0) {
$parameters = array_merge($this->query, $parameters);
}
return rtrim($this->path
.(Str::contains($this->path, '?') ? '&' : '?')
.http_build_query($parameters, '', '&')
.$this->buildFragment(), '?');
}
improved Aksi answer
app/Services/CustomLengthAwarePaginator.php
<?php
namespace App\Services;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
class CustomLengthAwarePaginator extends LengthAwarePaginator
{
/**
* Get the URL for a given page number.
*
* #param int $page
* #return string
*/
public function url($page)
{
if ($page <= 0) {
$page = 1;
}
// If we have any extra query string key / value pairs that need to be added
// onto the URL, we will put them in query string form and then attach it
// to the URL. This allows for extra information like sortings storage.
$parameters = ($page > 1) ? [$this->pageName => $page] : [];
if (count($this->query) > 0) {
$parameters = array_merge($this->query, $parameters);
}
return $this->path()
. (count($parameters) > 0
? (Str::contains($this->path(), '?') ? '&' : '?')
: '')
. Arr::query($parameters)
. $this->buildFragment();
}
}
in AppServiceProvider.php or another
public function boot()
{
app()->bind(LengthAwarePaginator::class, CustomLengthAwarePaginator::class);
}
add canonical tag in if you care SEO
i don't have any idea...
if($pageNum==1){
return redirect()->to($path);
}
$pageNum id get Request or urself Class
$path = "/jobs"
In your routes, give a name to the route which leads to your function:
Route::get('/yourRoute','YourController#foo')->name('yourRouteName');
Then, In your function in the controller, use this:
public function foo() {
if( request()->page=='1' )
{
return redirect()->route('yourRouteName',[$id]);
}
else {
// Your function content
}
}
I think you can try this:
{{ $jobs->links() }}
To
{{ $jobs->nextPageUrl() }}
Hope this work for you!
You can simply modify the view like this:
#if ($page==1)
{{ $page }}
#else
{{ $page }}
#endif
I found the following works and takes into account page=10 issues:
After you export your pagination views, make the following 2 modifications:
MODIFICATION 1
Replace this:
{{ $paginator->previousPageUrl() }}
with this:
{{ preg_replace('/(?:(&|\\?)page=[1])(?!\\d)/ui','', $paginator->previousPageUrl()) }}
MODIFICATION 2
Replace this:
{{ $url }}
with this:
{{ preg_replace('/(?:(&|\\?)page=[1])(?!\\d)/ui','', $url ) }}
I've done it this way:
{{ $posts->currentPage() == 2 ? route('home') : $posts->previousPageUrl() }}
Just create a Middleware by using this command
php artisan make:middleware RemovePageQuery
Write this code inside middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class RemovePageQuery
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if ($request->page == 1)
{
return redirect(url()->current());
}
return $next($request);
}
}
Add middleware global in App/Http/Kernel.php inside $middleware.
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\RemovePageQuery::class, //Here
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
I was searching to fix similar issue to prevent content duplication but for symfony not laravel. I fixed it in my way and decided to publish it. May be it will help somebody.
In symfony I use KNP paginator. It provides paginator template, like this:
{{ page }}
'Query' array contains route params and the task is to remove page param if it == 1.
Very simple:
{% set page_seo = (page == 1) ? null : page %}
{{ page }}
Actually - set to null page if it == 1.
By this way you can remove form field from the request in Laravel Controller. It will work 100%. You will get in request without page param.
$request->request->remove('page');
Better answer than #SaidbakR has provided, as I it's not right. Why? Because you will receive link for the same page as URL will be empty. And for example if you make request from url?page=2, you'll get link to same url?page=2 where link to first page must appear.
My easy variant:
Also publish pagination views: php artisan vendor:publish --tag=laravel-pagination
Go to /views/vendor/pagination/default.blade.php
Add elseif case to previous page link:
#elseif($paginator->currentPage() === 2)
<li class="pagination-next-prev link">
<a class="prev page-url" href="?{{ http_build_query(Arr::except(Request::query(), 'page')) }}" rel="prev"
aria-label="#lang('pagination.previous')">
Prev
</a>
</li>
#else
Add similar elseif to array of links:
#elseif(1 === $page)
<li class="page">
<a class="page-url" href="?{{ http_build_query(Arr::except(Request::query(), 'page')) }}">
{{ $page }}
</a>
</li>
#else
This case is also working if you are using ajax request to another page for pagination.
Done
You will have link to the page you opened, but without params. Yes it will be showed as ? in the URL, but it's fair price to avoid duplications of URLs, as link with just question mark for google same as without.

Controller Method not defined error - Laravel 5.1

I am trying to display the currently logged in username, as a link to the user info, in my main navigation. I can get the name to display, so I am pulling the info from the db, and passing it to the view OK. But, when I try to make it a link, I get the method not defined error.
Here is how I pass the user info to the navigation (as the var $userInfo):
public function index()
{
$Clients = \Auth::user()->clients()->get();
$userInfo = \Auth::user();
return view('clients.index', compact('Clients', 'userInfo'));
}
Here is the relevant bit from my navigation:
<ul class="nav navbar-nav">
<li>{!! link_to_action('AuthController#show', $userInfo->username, [$userInfo->id]) !!}</li>
</ul>
The method from my controller:
protected function show($id)
{
$userInfo = User::findOrFail($id);
return view('users.show', compact('userInfo'));
}
And, the route definition:
// User Display routes
Route::get('auth/{id}', 'Auth\AuthController#show');
Here is the error I get:
Action App\Http\Controllers\AuthController#show not defined.
Can anyone tell me what I am missing?
First, you need to make your AuthController::show() method public:
public function show($id)
{
$userInfo = User::findOrFail($id);
return view('users.show', compact('userInfo'));
}
Second, as your controllere is in App\Http\Controllers\Auth namespace, you need to use the **Auth** prefix in the view:
<ul class="nav navbar-nav">
<li>{!! link_to_action('Auth\AuthController#show', $userInfo->username, [$userInfo->id]) !!}</li>
</ul>

Categories