If I have two route groups (for simple prefixing of routes) is it possible to Route::bind per just that group?
When I do the following:
Route::group( array('prefix'=>'pre1'), function(){
Route::bind('items', function( $value, $route ){
$item = Item::find( $value );
if( !$item ) App::abort( 404 );
return $item;
})
Route::resource('items', .... );
})
Route::group( array('prefix'=>'pre2'), function(){
//put bind for users here...
Route::bind('items', function( $value, $route ){
$user_id = $route->parameter('users')->getAttribute('id');
$item = Item::where('id', $value)->whereUserId( $user_id );
if( !$item ) App::abort( 404 );
return $item;
})
Route::resource('users.items', ....)
})
The first bind to 'items' is overridden by the last one declared. I would rename the 'items' to something else, but nested Resource routes are auto generated by laravel.
Ie the first route is
/items/{items}
where the second is
/users/{users}/items/{items}
I would simply rename the end routes, but they make sense with regards to the resources being used from an admin having permissions on one resource and users on the other.
A couple of things. Firstly you dont need this code
Route::bind('items', function( $value, $route ){
$item = Item::find( $value );
if( !$item ) App::abort( 404 );
return $item;
})
You just need
Route::bind('items', 'Item')
It will automatically throw a 404 if it cant bind the Item model at runtime.
Secondly, you wont be able to do what you want (have two different bindings of the same name) - but there are two options.
Option 1 is just explicitly have all your routes defined in your routes file, and dont use Route::resource(). This article from Phil Sturgeon gives an excellent explanation of why you should just define each route manually.
The second Option is just to use the main Items route binding, but add a filter to the user items. Something like this:
Route::group( array('prefix'=>'pre2', 'before' => 'user.item'), function(){
Then define a filter which checks if the item belongs to the user
Route::filter('user.item', function($route, $request)
{
if ($route->parameter('item')->user_id !== Auth::user()->id)
{
App::abort(404);
}
});
Related
What is the most simple way to store custom attributes such as page titles or other key value pairs that may be attached to a route?
For example, say I want to add my own metadata data to:
Route::get('/themetest', [MyController::class, 'list'])->name('themetest');
I thought I could add a route macro to save metadata to be retrieved later using an addMetadata method like
Route::get('/themetest', [MyController::class, 'list'])->name('themetest')->addMetadata('title' => 'Page Title');
Is that possible? Doesn't seem like it is.
Is there a standard way to store this type of info? Or, any practical way? I thought maybe I could store them using default(), but that could change the default parameters for a controller function.
You could use the 'action' array of the Route to store this information if you had to:
// In a Service Provider # boot
Illuminate\Routing\Route::macro('addMetaData', function ($key, $value) {
$this->action['meta'][$key] = $value;
return $this;
});
Illuminate\Routing\Route::macro('getMetaData', function ($key = null) {
return is_null($key)
? $this->getAction('meta')
: $this->getAction('meta.'. $key);
});
// Route definition
Route::get('/themetest', [MyController::class, 'list'])
->name('themetest')
->addMetaData('title', 'Page Title');
// Controller method (Route action)
public function list(Request $request)
{
dump($request->route()->getMetaData('title'));
}
I am returning an API response inside a Categories controller in Laravel 5.5 like this...
public function get(Request $request) {
$categories = Category::all();
return Response::json(array(
'error' => false,
'categories_data' => $categories,
));
}
Now I am trying to also have the option to return a specific category, how can I do this as I am already using the get request in this controller?
Do I need to create a new route or can I modify this one to return a specific category only if an ID is supplied, if not then it returns all?
Better case is to create a new route, but you can also change the current one to retrieve all models if the parameter is not supplied. You first gotta choose which approach you will be using. For splitting it into multiple calls you can see Resource controllers and for using one method you can follow Optional Route Parameters
It will be much cleaner if you will create another route. For example
/categories --> That you have
/categories/{id} -> this you need to create
And then add method at same controller
public function show($id) {
$categories = Category::find($id);
return Response::json(array(
'error' => false,
'categories_data' => $categories,
));
}
But if you still want to do it at one route you can try something like this:
/categories -> will list all categories
/categories?id=2 -> will give you category of ID 2
Try this:
public function get(Request $request) {
$id = $request->get('id');
$categories = $id ? Category::find($id) : Category::all();
return Response::json(array(
'error' => false,
'categories_data' => $categories,
));
}
Hello I am trying to do it like this but it's getting all the routes, I only want the routes from a specific group(s).
This is my code:
<?php
$routes = Routes::getRoutes();
#foreach($routes as $route)
{{ $route->getPath() }}
#endforeach`
Thanks in advance!
Let's create some routes without any groups
Route::get('/', function () {
return view('welcome');
});
Route::get('/load', 'defaultController#load');
Now we'll create some routes with groups
Route::group(['as' => 'admin'], function () {
Route::get('users', function () {
return "users route";
});
Route::get('ravi', function () {
return "ravi route";
});
Now we are going to create a route in this group which will look for the admin group and print all routes that exist in this group.
Route::get('kumar', function () {
$name = 'admin';
$routeCollection = Route::getRoutes(); // RouteCollection object
$routes = $routeCollection->getRoutes(); // array of route objects
Now in our route object, we will look for our named route by filtering the array.
$grouped_routes = array_filter($routes, function($route) use ($name) {
$action = $route->getAction(); // getting route action
if (isset($action['as'])) {
// for the first level groups, $action['as']
// will be a string
// for nested groups, $action['as'] will be an array
if (is_array($action['as'])) {
return in_array($name, $action['as']);
} else {
return $action['as'] == $name;
}
}
return false;
});
// Here we will print the array containing the route objects in the 'admin' group
dd($grouped_routes);
});
});
Now you can copy and paste this in your route folder and you will be able to see the output by hitting your_project_public_folder_url/kumar
I took help from this answer Answer of patricus
Say I have the following route:
Route::any('/door1/{options?}', array(
'as' => 'path',
'after' => 'somefilter',
'uses' => 'DoorController#path'
))
where my after filter has the job to increment some value on a DB Table. The filter would look something like this:
Route::filter('somefilter', function($route, $request, $response, $action)
{
try
{
//...
IncrementDBTable();
//...
}
catch(Exception $e)
{
return Redirect::home();
}
});
As for the controller,
if the options parameter is valid then all is fine and a specific view is called, otherwise the options are "corrected" and a redirect to the same route is performed:
public function path($options='option1,option2')
{
if( !this->isValidOptions($options) ) {
$options = this->correctOptions($options);
return Redirect::route( 'path', array(
'options' => $options
) );
}
// everything fine:
return View::make( 'door' );
}
My problem is, and for the case the options are wrong, the after filter is called twice, meaning the increment on the DB table is performed also twice. How can I "ignore" the after filter for the case the options are wrong? One possible solution of course is to also validate the options inside the filter function, but I wouldn't like to replicate code (this is just a simple example but things would get ugly if the validate options function would involve a lot more of code and possibly a bunch of db queries).
Looking at your code, you can skip the Redirect after correcting the options and just continue down the function instead. I don't see a reason for redirecting after correcting the options. That will also solve your problem about the after filter being called twice.
i am just starting to learn laravel. And found a small sample project from nettuts+ ( url shortner ). it works well but only problem i face is that (:any) route doesn't work.
Here are three routes i have in file.
Route::get('/', function()
{
return View::make('home.index');
});
Route::post('/', function()
{
$url = Input::get('url');
// Validate the url
$v = Url::validate(array('url' => $url));
if ( $v !== true ) {
return Redirect::to('/')->with_errors($v->errors);
}
// If the url is already in the table, return it
$record = Url::where_url($url)->first();
if ( $record ) {
return View::make('home.result')
->with('shortened', $record->shortened);
}
// Otherwise, add a new row, and return the shortened url
$row = Url::create(array(
'url' => $url,
'shortened' => Url::get_unique_short_url()
));
// Create a results view, and present the short url to the user
if ( $row ) {
return View::make('home.result')->with('shortened', $row->shortened);
}
});
Route::get('(:any)', function($shortened)
{
// query the DB for the row with that short url
$row = Url::where_shortened($shortened)->first();
// if not found, redirect to home page
if ( is_null($row) ) return Redirect::to('/');
// Otherwise, fetch the URL, and redirect.
return Redirect::to($row->url);
});
first two routes work fine but third one never gets activated. it only works if i call it with index.php in url. Like /index.php/abc whereas it should work for /abc too. And fyi, i have removed index.php setting from application config file too.
Can you please help ho to fix it?
Change the route declaration from '(:any)'
Route::get('(:any)', function($shortened){ //... });
to ('/(:any)')
Route::get('/(:any)', function($shortened){ //... });