Laravel check if route for given url exists inside routes - php

By providing a URL I would like to know if there's any way to determine if the URL exists in my Laravel application (in comparison to "How can I check if a URL exists via Laravel?" which wants to check an external URL)?
I tried this but it always tells me the URL doesn't match:
$routes = \Route::getRoutes();
$request = \Request::create('/exists');
try {
$routes->match($request);
// route exists
} catch (\Symfony\Component\HttpKernel\Exception\NotFoundHttpException $e){
// route doesn't exist
}

simply use if Route::has('example')

Route::has('route_name') check if the route exists (on routes files).
Example: test some app routes
<?php
namespace Tests\Feature;
use App\Models\Users\User;
use Illuminate\Support\Facades\Route;
use Tests\TestCase;
class RouteTest extends TestCase
{
private $routes = [
'home',
'users.index',
'employees.index',
'logs.index',
];
public function setUp(): void
{
parent::setUp();
// Put an admin on session
$adminUser = User::getFirstAdmin();
$this->actingAs($adminUser);
}
public function testRoutes()
{
foreach ($this->routes as $route) {
$this->assertTrue(Route::has($route));
$response = $this->get(route($route));
$response->assertStatus(200);
}
}
}
Edition
The fast way, check with tinker typing on the terminal:
php artisan tinker
then:
Route::has('your_route')
or
route('your_route')

In one of my Laravel application, I achieved it by doing the following
private function getRouteSlugs()
{
$slugs = [];
$routes = Route::getRoutes();
foreach ($routes as $route)
{
$parts = explode('/', $route->uri());
foreach ($parts as $part)
{
$slug = trim($part, '{}?');
$slugs[] = $slug;
}
}
return array_unique($slugs);
}
This function would help to get all the slugs that are registered within Laravel and then with a simple in_array you can check if that slug has been reserved.
EDIT
Based on your comment, you can extend the following function
private function getRouteSlugs()
{
$slugs = [];
$routes = Route::getRoutes();
foreach ($routes as $route)
{
$slugs[] = $route->uri();
}
return array_unique($slugs);
}
That will get you an array of items as such:
0 => "dashboard/news"
1 => "dashboard/post/news"
2 => "dashboard/post/news/{id}"
3 => "dashboard/post/news"
It should be easy enough from here to compare.

Related

Slim Framework 3 - using router in a global template

I'm relatively new to Slim Framework 3. One thing I'm trying to understand is how to use the router, $this->router, in a "global" template.
What I mean by this is a template such as a navigation menu - something that appears on every page.
For templates I'm using the "php-view" library as per the example tutorial which I installed with:
composer require slim/php-view
In my templates directory I have a file called nav.php where I want to output my links.
I understand how to call the router like so
Sign Up
But... the example tutorial only shows how you would pass that link from 1 individual place, e.g. $app->get('/sign-up' ... })->setName("sign-up");
How can you use the router globally in any template, without passing it into every individual URL route as a parameter?
I'm more familiar with frameworks like CakePHP where there is an "AppController" which allows you to set things globally, i.e. available in every request. I don't know if this is how it's done in Slim but this is the effect I'm after.
Well, you can pass it as template variable.
When you instantiate or register PhpRenderer in a container, you have multiple options to define a "global" variable, i.e. a variable that is accessible in all of your templates:
// via the constructor
$templateVariables = [
"router" => "Title"
];
$phpView = new PhpRenderer("./path/to/templates", $templateVariables);
// or setter
$phpView->setAttributes($templateVariables);
// or individually
$phpView->addAttribute($key, $value);
Assuming you're registering PhpRenderer via Pimple:
<?php
// Create application instance
$app = new \Slim\App();
// Get container
$container = $app->getContainer();
// Register PhpRenderer in the container
$container['view'] = function ($container) {
// Declaring "global" variables
$templateVariables = [
'router' => $container->get('router')
];
// And passing the array as second argument to the contructor
return new \Slim\Views\PhpRenderer('path/to/templates/with/trailing/slash/', $templateVariables);
};
<?php namespace App\Helpers;
/********************/
//LinksHelper.php
/********************/
use Interop\Container\ContainerInterface;
class LinksHelper
{
protected $ci;
public function __construct(ContainerInterface $container){
$this->ci = $container;
}
public function __get($property){
if ($this->ci->has($property)) {
return $this->ci->get($property);
}
}
public function pathFor($name, $data = [], $queryParams = [], $appName = 'default')
{
return $this->router->pathFor($name, $data, $queryParams);
}
public function baseUrl()
{
if (is_string($this->uri)) {
return $this->uri;
}
if (method_exists($this->uri, 'getBaseUrl')) {
return $this->uri->getBaseUrl();
}
}
public function isCurrentPath($name, $data = [])
{
return $this->router->pathFor($name, $data) === $this->uri->getPath();
}
public function setBaseUrl($baseUrl)
{
$this->uri = $baseUrl;
}
}
?>
<?php
/********************/
//dependencies.php
/********************/
$container['link'] = function ($c) {
return new \App\Helpers\LinksHelper($c);
};
// view renderer
$container['view'] = function ($c) {
$settings = $c->get('settings');
$view = new App\Views\MyPhpRenderer($settings['renderer']['template_path']);
$view->setLayout('default.php');
//$view->addAttribute('title_for_layout', $settings['title_app'] .' :: ');
$view->setAttributes([
'title_for_layout'=>$settings['title_app'] .' :: ',
'link' => $c->get('link')
]);
return $view;
};
?>
<?php
/********************/
//routes.php
/********************/
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
$app->get('/', function (Request $request, Response $response, array $args) {
return $this->view->render($response, 'your_view.php');
})->setName('home');
?>
<?php
/********************/
//your_view.php
/********************/
?>
Home
You should create a new class, e.g. MainMenu, and there you should create an array with all paths for menu. Object of MainMenu should return an array with labels and paths and then you can pass that array to your view:
$menu = (new MainMenu())->buildMenu();
$response = $this->view->render($response, "index.phtml", [
'menu' => $menu
]);
Then in your *.phtml file you have access to the $menu variable. But what if you do not want repeat that code in each route?
Use middlewares. You can pass a variable from middleware using
$request = $request->withAttribute('foo', 'bar');
and retrieve
$foo = $request->getAttribute('foo');

thephpleague route - locale in uri

Hi I've switched to thephpleague route from a self written routingengine. My question is: Can i access wildcard variables outside of the route action method?
Example
Routing Part:
$router = new League\Route\RouteCollection;
$router->addRoute('GET', '{locale}/{controller}/{action}', '\Backend\Controller\{controller}Controller::{action}');
$dispatcher = $router->getDispatcher();
//making a call with, for example, '/en/foo/bar', or '/de/foo/bar'
$response = $dispatcher->dispatch($oRequest->getMethod(), $oRequest->getPathInfo());
$response->send();
Controller part
class FooController extends AppController {
public function __construct() {
//<---- here i want to access the {locale} from the URI somehow
}
public function bar(Request $request, Response $response, array $args) {
// $args = [
// 'locale' => 'de', // the actual value of {locale}
// 'controller' => 'foo' // the actual value of {controller}
// 'action' => 'bar' // the actual value of {bar}
// ];
}
}
I could not find anything in the docs route.thephpleague
I'm using "league/route": "^1.2"
I think by default, you can only call methods in controller classes statically and when you do that the controller's constructor will not be called automatically. And also you can't use the route's wildcards to dynamically call controllers.
Please take note that this is not secure, but you should still be able to do what you want to happen with a Custom Strategy in league/route like this:
Controller
class TestController {
public function __construct($args) {
//the wildcards will be passed as an array in the constructor like this
$this->locale = $args['locale'];
}
public function check(Request $request, Response $response, array $args) {
// $args = [
// 'locale' => 'de', // the actual value of {locale}
// 'controller' => 'Test' // the actual value of {controller}
// 'action' => 'check' // the actual value of {action}
// ];
return $response;
}
}
Custom Strategy
class CustomStrategy implements StrategyInterface {
public function dispatch($controller, array $vars)
{
$controller_parts = [];
foreach($controller as $con){
foreach ($vars as $key => $value) {
$placeholder = sprintf('{%s}', $key);
$con = str_replace($placeholder, $value, $con);
}
$controller_parts[] = $con;
}
//the controller will be instantiated inside the strategy
$controllerObject = new $controller_parts[0]($vars);
//and the action will be called here
return $controllerObject->$controller_parts[1](Request::createFromGlobals(),new Response(),$vars);
}
}
Routing with custom strategy integration
$router = new League\Route\RouteCollection;
$router->setStrategy(new CustomStrategy()); //integrate the custom strategy
$router->addRoute('GET', '/{locale}/{controller}/{action}', '{controller}Controller::{action}');
$dispatcher = $router->getDispatcher();
//if your url is /en/Test/check, then TestController->check() will be called
$response = $dispatcher->dispatch($oRequest->getMethod(), $oRequest->getPathInfo());
$response->send();

How do I access the Router through an artisan command?

I need access to the RouteCollection that Laravel possesses when it gets ran normally and all ServiceProviders are booted. I need the RouteCollection because a legacy app needs to know if Laravel has the particular route, so if it doesn't, legacy code can take care of the route.
I figure if I can somehow get a hold of Illuminate\Routing\Router in an artisan command, I could simply call the getRoutes() function and output a JSON file containing an array of all the routes. Then when the legacy code needs to determine if Laravel supports the Route, it could read that file.
In order to do that though, I need access to the Router class. Not sure how to accomplish that... I looked at Illuminate\Foundation\Console\RoutesCommand source code and I can't figure out how it works. What's really odd is it looks like the Router class is being injected, but when I do Artisan::resolve('MyCommand'), I get an empty RouteCollection.
EDIT
I never did figure out how to accomplish this question, but for those in a similar situation, I found this works for the most part, although I'm not sure how bad the overhead is starting Laravel each request just to check the routes. Right now it doesn't seem like that much.
// Start laravel, so we can see if it should handle the request
require_once(__DIR__.'/laravel/bootstrap/autoload.php');
$app = require_once(__DIR__.'/laravel/bootstrap/start.php');
$app->boot();
// Create the fake request for laravel router
$request = Request::createFromGlobals();
$request->server->set('PHP_SELF', '/laravel/public/index.php/'.$_REQUEST['modname']);
$request->server->set('SCRIPT_NAME', '/laravel/public/index.php');
$request->server->set('SCRIPT_FILENAME', __DIR__.'/laravel/public/index.php');
$request->server->set('REQUEST_URI', '/laravel/public/'.$_REQUEST['modname']);
$routes = Route::getRoutes();
foreach($routes as $route) {
if ($route->matches($request)) {
$app->run($request);
exit;
}
}
Here is a simplified implementation
JsonRoutes.php
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Illuminate\Routing\Route;
use Illuminate\Routing\Router;
class JsonRoutes extends Command {
protected $name = 'routes:json';
protected $description = 'Spits route information in JSON format.';
protected $router;
protected $routes;
public function __construct(Router $router) {
parent::__construct();
$this->router = $router;
$this->routes = $router->getRoutes();
}
public function fire() {
$result = [];
foreach ($this->routes as $route) {
$result[] = [
'methods' => implode(',', $route->methods()),
'host' => $route->domain(),
'uri' => $route->uri(),
'name' => $route->getName(),
'action' => $route->getActionName(),
];
}
$file = $this->option('file');
if ($file) {
File::put($file, json_encode($result));
return;
}
$this->info(json_encode($result));
}
protected function getArguments() { return []; }
protected function getOptions() {
return [
['file', 'f', InputOption::VALUE_OPTIONAL, 'File name.'],
];
}
}
You register it in artisan.php:
Artisan::resolve('JsonRoutes');
Sample usage:
Spit it out to stdout
$ artisan routes:json
[{"methods":"GET,HEAD","host":null,"uri":"\/","name":null,"action":"Closure"}]
Write it to a file
$ artisan routes:json -f /tmp/routes.json

Laravel Creating Dynamic Routes to controllers from Mysql database

I have the following table: group_pages in mysql database with page name route name :
id name route
--------------------
0 About about
1 Contact contact
2 Blog blog
what I am trying to do is to create dynamic routes in my : routes.php ?
Where if I go to for example: /about it will go to AboutController.php ( which will be created dynamically) is that possible? is it possible to create a dynamic controller file?
I am trying to create dynamic pages routes that links to a controller
example i want to generate this dynamically in my routes.php
Route::controller('about', 'AboutController');
Route::controller('contact', 'ContactController');
Route::controller('blog', 'BlogController');
This is not the right way to create dynamic pages instead, you should use a database and keep all pages in the database. For example:
// Create pages table for dynamic pages
id | slug | title | page_content
Then create Page Eloquent model:
class Page extends Eloquent {
// ...
}
Then create Controller for CRUD, you may use a resource controller or a normal controller, for example, normally a PageController:
class PageController extends BaseController {
// Add methods to add, edit, delete and show pages
// create method to create new pages
// submit the form to this method
public function create()
{
$inputs = Input::all();
$page = Page::create(array(...));
}
// Show a page by slug
public function show($slug = 'home')
{
$page = page::whereSlug($slug)->first();
return View::make('pages.index')->with('page', $page);
}
}
The views/page/index.blade.php view file:
#extends('layouts.master')
{{-- Add other parts, i.e. menu --}}
#section('content')
{{ $page->page_content }}
#stop
To show pages create a route like this:
// could be page/{slug} or only slug
Route::get('/{slug}', array('as' => 'page.show', 'uses' => 'PageController#show'));
To access a page, you may require url/link like this:
http://example.com/home
http://example.com/about
This is a rough idea, try to implement something like this.
After spending 2 hours, digging through google and Laravel source, I came up with this solution, which I think works the best and looks the cleanest. No need for redirects and multiple inner requests.
You add this route at the very bottom of routes files.
If no other routes are matched, this is executed. In the closure, you decide which controller and action to execute.
The best part is - all route parameters are passed to action, and method injection still works. The ControllerDispatcer line is from Laravel Route(r?) class.
My example would handle 2 cases - first checks if user exists by that name, then checks if an article can be found by the slug.
Laravel 5.2 (5.3 below)
Route::get('{slug}/{slug2?}', function ($slug) {
$class = false;
$action = false;
$user = UserModel::where('slug', $slug)->first();
if ($user) {
$class = UserController::class;
$action = 'userProfile';
}
if (!$class) {
$article= ArticleModel::where('slug', $slug)->first();
if ($article) {
$class = ArticleController::class;
$action = 'index';
}
}
if ($class) {
$route = app(\Illuminate\Routing\Route::class);
$request = app(\Illuminate\Http\Request::class);
$router = app(\Illuminate\Routing\Router::class);
$container = app(\Illuminate\Container\Container::class);
return (new ControllerDispatcher($router, $container))->dispatch($route, $request, $class, $action);
}
// Some fallback to 404
throw new NotFoundHttpException;
});
5.3 has changed how the controller gets dispatched.
Heres my dynamic controller example for 5.3, 5.4
namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use Illuminate\Routing\ControllerDispatcher;
use Illuminate\Routing\Route;
class DynamicRouteController extends Controller
{
/**
* This method handles dynamic routes when route can begin with a category or a user profile name.
* /women/t-shirts vs /user-slug/product/something
*
* #param $slug1
* #param null $slug2
* #return mixed
*/
public function handle($slug1, $slug2 = null)
{
$controller = DefaultController::class;
$action = 'index';
if ($slug1 == 'something') {
$controller = SomeController::class;
$action = 'myAction';
}
$container = app();
$route = $container->make(Route::class);
$controllerInstance = $container->make($controller);
return (new ControllerDispatcher($container))->dispatch($route, $controllerInstance, $action);
}
}
Hope this helps!
try
Route::get('/', ['as' => 'home', 'uses' => 'HomeController#index']);
$pages =
Cache::remember('pages', 5, function() {
return DB::table('pages')
->where('status', 1)
->lists('slug');
});
if(!empty($pages))
{
foreach ($pages as $page)
{
Route::get('/{'.$page.'}', ['as' => $page, 'uses' => 'PagesController#show']);
}
}
There is a component available, which you can use to store routes in a database. As an extra advantage, this component only loads the current active route, so it improves performance, since not all routes are loaded into the memory.
https://github.com/douma/laravel-database-routes
Follow the installation instructions provided in the readme.
Storing routes in the database
The only thing needed here is injecting the RouteManager into for example a cli command. With addRoute can tell the RouteManager to store the route into the database. You can easily change this code and use your own repository of pages or other data to construct the routes.
use Douma\Routes\Contracts\RouteManager;
class RoutesGenerateCommand extends Command
{
protected $signature = 'routes:generate';
private $routeManager;
public function __construct(RouteManager $routeManager)
{
$this->routeManager = $routeManager;
}
public function handle()
{
$this->routeManager->addRoute(
new Route('/my-route', false, 'myroute', MyController::class, 'index')
);
}
}
Run this cli command every 2-5 minutes or after a change in your data to make sure the routes are recent.
Register the RouteMiddleware in App\Http\Kernel.php
\Douma\Routes\Middleware\RouteMiddleware::class
Empty your web.php
If you have defined any Laravel route, make sure to empty this file.
Using routes from the database
You can use the route in blade:
{{ RouteManager::routeByName('myroute')->url() }}
Or you can inject the RouteManager-interface anywhere you like to obtain the route:
use Douma\Routes\Contracts\RouteManager;
class MyClass
{
public function __construct(RouteManager $routeManager)
{
$this->routeManager = $routeManager;
}
public function index()
{
echo $this->routeManager->routeByName('myroute')->url();
}
}
For more information see the readme.
We can make dynamic route by this way
// Instanciate a router class.
$router = app()->make('router');
Get Route value from Database
// For route path this can come from your database.
$paths = ['path_one','path_two','path_three'];
Then iterate the value to make dynamic route
// Then iterate the router "get" method.
foreach($paths as $path){
$router->resource($path, 'YourController');
}
You can use GET|POST|PUT|PATCH|DELETE methods also
// Then iterate the router "get" method.
foreach($paths as $path){
$router->get($path, 'YourController#index')->name('yours.index');
}
You can do something like this. It works perfect for me
use Illuminate\Container\Container;
use Illuminate\Routing\ControllerDispatcher;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Illuminate\Routing\Route as MainRoute;
use Illuminate\Routing\Router;
Route::group([
'prefix' => config('app.payment.route_prefix'),
'namespace' => config('app.payment.route_namespace'),
'middleware' => config('app.payment.route_middleware')
], function (Router $router) {
/**
* Resolve target payment method controller
*
* #param $key Example key [paypal]
* #param $action
* #return mixed
*/
$resolver = function ($key, $action) {
$route = app(MainRoute::class);
$container = app(Container::class);
// Generate App\\Http\Controllers\PaymentMethods\PaypalController
$controller = app(sprintf('%s\\%sController', config('app.payment.route_namespace'), ucfirst($key)));
return (new ControllerDispatcher($container))->dispatch($route, $controller, $action);
};
$router->post('{key}/error', function ($key) use ($resolver) {
return $resolver($key, 'error');
});
$router->post('{key}/success', function ($key) use ($resolver) {
return $resolver($key, 'success');
});
$router->get('{key}', function ($key) use ($resolver) {
return $resolver($key, 'index');
});
});

url_for in backend for frontend - Symfony

i made in frontend nice url with symfony routing.yml. In frontend i can use for example:
url_for('#news', $news);
this generate for me:
http://mysite.com/frontend_dev.php/news/nice-title/1
but if i use this in backend i have:
http://mysite.com/backend_dev.php/news/nice-title/1
is possible generate these link in backend for frontend?
In apps/frontend/config/frontendConfiguration.class.php use something like this:
class frontendConfiguration extends sfApplicationConfiguration
{
protected $backendRouting = null;
public function generateBackendUrl($name, $parameters = array(), $absolute = false)
{
return sfConfig::get('app_site_url').$this->getBackendRouting()->generate($name, $parameters, $absolute);
}
public function getBackendRouting()
{
if (!$this->backendRouting )
{
$this->backendRouting = new sfPatternRouting(new sfEventDispatcher());
$config = new sfRoutingConfigHandler();
$routes = $config->evaluate(array(sfConfig::get('sf_apps_dir').'/backend/config/routing.yml'));
$this->backendRouting->setRoutes($routes);
}
return $this->backendRouting;
}
}
use it like this:
$sf_context->getConfiguration()->generateBackendUrl('my_custom_route', array('id' => 1), true)
More information here: http://symfony.com/blog/cross-application-links

Categories