Handling generated urls in laravel? - php

I'm making a link shortener as part of a school project,
Route::get('{short_url}', array('uses' => 'UrlController#shortUrlRedirect');
This function works fine alone, but as I have other functions such as
Route::post('register', array('uses' => 'HomeController#doRegister'));
whenever example.com/anylink
is now used, it is handled by one function alone.
A working solution I found would be to do something like:
Route::get('url/{short_url}', array('uses' => 'UrlController#shortUrlRedirect');
But of course with a link shortener, the goal is to have as little characters as possible.
Any ideas of a possible way to handle this issue within laravel?

The earlier or 'higher' in the routes.php file is the route, the more priority it gets, so if you define two identical routes or two routes that match one pattern, like in your example, the first one will be executed.
So you should define register route earlier, as it should not be overriden by the {short_url}.
Here is the explanation: Routes: First in, first out
TL;DR: Laravel receives a request, and uses the URI of the request to find a matching pattern iterating the routes file, when it finds one, it break;s the loop.

Related

Add static route to a route the is supposed to be dynamic?

In my routes file, web.php I have,
Route::get('/m/{game}', 'CommentController#index')->name('game');
Route::get('/m/{game?}', 'GameController#all')->name('all');
But can I add "static" routes, for example:
Route::get('/m/snes', 'GameController#snes')->name('snes')
Arrange your routing to make the static at top, so laravel routing will find the static first rather than go to the /m/{game} and /m/{game?}
Put this route:
Route::get('/m/snes', 'GameController#snes')->name('snes');
Before the other routes to make it work.
You have to put the static route on top of your dynamic routes.
Route::get('/m/snes', 'GameController#snes')->name('snes')
Route::get('/m/{game}', 'CommentController#index')->name('game');
Route::get('/m/{game?}', 'GameController#all')->name('all');
You have to put the static route before the other routes. What happens is that when Laravel is checking which route to use when you go to m/snes, it checks the routes until it reaches a match. So if you have:
Route::get('/m/{game}', 'CommentController#index')->name('game');
Route::get('/m/{game?}', 'GameController#all')->name('all');
Route::get('/m/snes', 'GameController#snes')->name('snes')
Laravel notices that the first route is valid, since m/snes means that "snes" can be the {game} variable.
If you put the static route on top, however:
Route::get('/m/snes', 'GameController#snes')->name('snes')
Route::get('/m/{game}', 'CommentController#index')->name('game');
Route::get('/m/{game?}', 'GameController#all')->name('all');
as soon as it reaches it it notices it's a match. And if your intended URL was something like m/n64 it keeps on searching until it finds a match (in this case, it would be the next route on the list). This is a common ocurrence on routing files, even across JS projects.

Laravel route not matching pattern

In my Laravel routes/web.php file I have defined the following two routes:
Route::get('transaction/{id}', ['uses' => 'PaynlTransactionController#show'])->name('transaction.show');
Route::get('transaction/{txId}', ['uses' => 'PaynlTransactionController#showByTxId'])->name('transaction.showByTxId');
In my RouteServicesProvider I have defined the following two patterns:
Route::pattern('id', '[0-9]+');
Route::pattern('txId', '/^(TX(1[0-9]\d|[2-9]\d\d)-(1[0-9]\d\d\d\d|[2-9]\d\d\d\d\d))$/');
Whenever I go to transaction/<id> the routing works correctly, as long as id is an integer. However, when I go to transaction/TX874-152268, for example, it doesn't match any route and I receive the NotFoundHttpException in RouteCollection.php error.
I've validated the txId regex and it gives a full match: https://regex101.com/r/kDZR4L/1
My question: how come only my id pattern is working correctly, whereas my txId pattern isn't?
In the route
Route::pattern('txId', '/^(TX(1[0-9]\d|[2-9]\d\d)-(1[0-9]\d\d\d\d|[2-9]\d\d\d\d\d))$/');
I had included the forward slashes at the start and end of the string. This should not be included when passing a pattern to Route::pattern. Thus the following works:
Route::pattern('txId', '^(TX(1[0-9]\d|[2-9]\d\d)-(1[0-9]\d\d\d\d|[2-9]\d\d\d\d\d))$');
Because the urls are both /transaction/{value} it will get the last on.
If you change /transaction/{txId} to /transaction/tx/{txId} it will be clear for the routes.
Routes can only get one, so when you assign the prefix (at this time /transaction) to both of the urls it doesn't work.
You can also use /transaction/TX{txId}, in your controller you can past TX before the txId variable.
public function showByTxId($txId) {
$txid = "TX".$txid;
}
Edit:
Remove the / add the start.
Route::pattern('txId', '^(TX(1[0-9]\d|[2-9]\d\d)-(1[0-9]\d\d\d\d|[2-9]\d\d\d\d\d))$');
Hope this works!

Route definition not working in Laravel

I have 2 routes in my routes file.
Route::get('/deals/{merchant_name}?c={deal_id}', ['uses' => 'dealsvisibleController#index']);
Route::get('/deals/{merchant_name}', ['uses' =>'dealsController#index']);
Both routes are calling on a different controller function. The first route is however not working.
I am trying this in a 3rd controller.
return redirect('deals/'.$merchant_name.'?c='.$deal_id);
However, when the page redirects, it is calling dealsController#index and not dealsvisibleController#index
Can someone help me with why this is happening.
Laravel's router considers only path when matching URLs to your routes. Therefore, if you redirect to deals/someMerchant?c=someDealId then it uses deals/someMerchant to match the URL.
You'll need to define the first route as deals/{merchant_name}/{deal_id} in order for this routing to work as you want it to.

Laravel - why not relative URL's?

I have been learning Laravel recently and I have seemingly missed a key point: why should relative links be avoided?
For example, I have been suggested to use URL::to() which outputs the full path of the page passed as a parameter - but why do this when you could just insert a relative link anyway? E.g., putting URL::to('my/page') into a <href> will just insert http://www.mywebsite.com/my/page into the <href>; but on my website href='my/page' works exactly the same. On my website I have based all relative URL's from the index.php file found in the public directory.
Clearly, I'm missing a key point as to why full paths are used.
I've found that using route() on named routes to be a much better practice. If at one point you decide, for example, that your admin panel shouldn't point to example.com/admin, but example.com/dashboard, you will have to sift through your entire code to find all references to Url::to("/admin"). With named routes, you just have to change the reference in routes.php
Example:
Route::get('/dashboard', ['as' => 'admin', 'uses' => 'AdminController#index']);
Now every time you need to provide a link to your admin page, just do this:
Admin
Much better approach, in my opinion.
This is even available in your backend, say in AdminController.php
// do stuff
return redirect()->route('admin');
http://laravel.com/docs/5.1/routing#named-routes
Neither absolute nor relative links should be used - it's advisable to use named routes like so:
Route::get('my/page', ['as' => 'myPage', function () {
// return something
}]);
or
Route::get('my/page', 'FooController#showPage')->name('myPage');
Then, generate links to pages using URL::route() method (aliased as route() in L5), which is available in Blade as well as in your backend code.
That way, you can change the path to your routes at any time without having to worry about breaking links anywhere in your application.

Add JSON routes for all routes to handle backend requests

I'm using Laravel 5.1 and am building a service that can be seen as JSON or HTML. This approach is already done by sites like reddit.
Example
Normal view: http://www.reddit.com/r/soccer
JSON view: http://www.reddit.com/r/soccer.json
As you can see, they simply add .json to an URL and the user is able to see the exact same content either as HTML or as JSON.
I now wanted to reproduce the same in Laravel, however I'm having multiple issues.
Approach 1 - Optional parameter
The first thing I tried was adding optional parameter to all my routes
Route::get('/{type?}', 'HomeController#index');
Route::get('pages/{type?}', 'PageController#index');
However, the problem I was facing here, is that all routes were caught by the HomeController, meaning /pages/?type=json as well as /pages?type=json were redirected to the HomeController.
Approach 2 - Route Grouping with Namespaces
Next I tried to add route groupings with namespaces, to seperate backend and frontend
Route::get('pages', 'PageController#index');
Route::group(['prefix' => 'json', 'namespace' => 'Backend'], function(){
Route::get('pages', 'PageController#index');
});
However, this doesn't work either. It does work, when using api as prefix, but what I want, is that I can add .json to every URL and get the results as json. How can I achieve that in Laravel?
You can apply regular expressions on your parameters to avoud such catch-all situation as you have for HomeController#index:
Route::get('/pages{type?}', 'PageController#index'->where('type', '\.json'));
This way it type will only match, if it is equal to .json.
Then, to access it in your controller:
class PageController {
public function index($type = null) {
dd($type);
}
}
and go to /pages.json

Categories